From 75443cd39eb37e876db146b6e1bb67924876e15d Mon Sep 17 00:00:00 2001 From: Odboy Date: Thu, 2 Jul 2026 21:16:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(clients):=20=E5=90=84=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E6=94=AF=E6=8C=81=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8CSpring=20Boot=202=20=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E4=BC=98=E5=8C=96=E9=80=89=E6=8B=A9=E6=80=A7?= =?UTF-8?q?=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile.springboot2: 更换基础镜像为阿里云私有镜像 - .NET/SpringBoot2 demo: 配置项改为从环境变量读取,支持 Docker 传参 - Spring Boot 2 客户端: @Value/@ConfigurationProperties 刷新优化为只刷新受变更 key 影响的字段 - README_DOCKERFILE.md: 标注 .NET 客户端 Docker 测试通过 --- Dockerfile.springboot2 | 5 ++- README_DOCKERFILE.md | 2 +- kenaito-conf-dotnet-client-demo/Program.cs | 13 +++--- .../src/main/resources/application.properties | 12 +++--- .../odboy/confcenter/ConfigcenterContext.java | 14 ++++--- .../InternalConfigChangeListener.java | 5 ++- .../confcenter/RefreshScopeRegistry.java | 42 +++++++++++-------- 7 files changed, 53 insertions(+), 40 deletions(-) diff --git a/Dockerfile.springboot2 b/Dockerfile.springboot2 index 993c493..578b207 100644 --- a/Dockerfile.springboot2 +++ b/Dockerfile.springboot2 @@ -1,5 +1,5 @@ # Build stage -FROM maven:3.9-eclipse-temurin-8 AS build +FROM registry.cn-shanghai.aliyuncs.com/odboy/kenaito-cicd:runtime-maven3-jdk21 AS build WORKDIR /src @@ -9,12 +9,13 @@ WORKDIR /src/kenaito-conf-springboot2-client RUN mvn clean install -DskipTests # Copy demo project and build +WORKDIR /src COPY kenaito-conf-springboot2-client-demo/ ./kenaito-conf-springboot2-client-demo/ WORKDIR /src/kenaito-conf-springboot2-client-demo RUN mvn clean package -DskipTests # Runtime stage -FROM eclipse-temurin:8-jre +FROM registry.cn-shanghai.aliyuncs.com/odboy/kenaito-cicd:runtime-jdk8 WORKDIR /app COPY --from=build /src/kenaito-conf-springboot2-client-demo/target/*.jar app.jar diff --git a/README_DOCKERFILE.md b/README_DOCKERFILE.md index 57de242..5a1eaca 100644 --- a/README_DOCKERFILE.md +++ b/README_DOCKERFILE.md @@ -62,7 +62,7 @@ curl http://localhost:8082/health docker stop python-demo ``` -## .NET 客户端 +## .NET 客户端(测试通过) ```bash docker build -f Dockerfile.dotnet -t kenaito-conf-dotnet-demo . diff --git a/kenaito-conf-dotnet-client-demo/Program.cs b/kenaito-conf-dotnet-client-demo/Program.cs index 46e19ee..f44172c 100644 --- a/kenaito-conf-dotnet-client-demo/Program.cs +++ b/kenaito-conf-dotnet-client-demo/Program.cs @@ -2,12 +2,13 @@ using Kenaito.Conf.Client; var config = new Config { - BaseUrl = "http://localhost:8080", - AppName = "kenaito-springboot-demo", - Env = "daily", - Version = "latest", - SyncInterval = 10, - CacheDir = "." + BaseUrl = Environment.GetEnvironmentVariable("KENAITO_BASE_URL") ?? "http://localhost:8080", + AppName = Environment.GetEnvironmentVariable("KENAITO_APP_NAME") ?? "kenaito-springboot-demo", + Env = Environment.GetEnvironmentVariable("KENAITO_ENV") ?? "daily", + Version = Environment.GetEnvironmentVariable("KENAITO_VERSION") ?? "latest", + SyncInterval = int.TryParse(Environment.GetEnvironmentVariable("KENAITO_SYNC_INTERVAL"), out var si) ? si : 10, + CacheDir = Environment.GetEnvironmentVariable("KENAITO_CACHE_DIR") ?? ".", + Token = Environment.GetEnvironmentVariable("KENAITO_TOKEN") }; var client = new Client(config); diff --git a/kenaito-conf-springboot2-client-demo/src/main/resources/application.properties b/kenaito-conf-springboot2-client-demo/src/main/resources/application.properties index 6e1814f..c2d9894 100644 --- a/kenaito-conf-springboot2-client-demo/src/main/resources/application.properties +++ b/kenaito-conf-springboot2-client-demo/src/main/resources/application.properties @@ -6,12 +6,12 @@ app.name=kenaito-conf-springboot2-client-demo app.version=1.0.0 # Kenaito Config Center -kenaito.config.baseUrl=http://localhost:8080 -kenaito.config.appName=kenaito-springboot-demo -kenaito.config.profile=daily -kenaito.config.version=latest -kenaito.config.token= -kenaito.config.sync-interval-seconds=10 +kenaito.config.baseUrl=${KENAITO_BASE_URL:http://localhost:8080} +kenaito.config.appName=${KENAITO_APP_NAME:kenaito-springboot-demo} +kenaito.config.profile=${KENAITO_ENV:daily} +kenaito.config.version=${KENAITO_VERSION:latest} +kenaito.config.token=${KENAITO_TOKEN:} +kenaito.config.sync-interval-seconds=${KENAITO_SYNC_INTERVAL:10} # Local configs (will be overridden by remote if exists) database.host=192.168.1.1 diff --git a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/ConfigcenterContext.java b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/ConfigcenterContext.java index 6b395c5..4a1f2e7 100644 --- a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/ConfigcenterContext.java +++ b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/ConfigcenterContext.java @@ -333,6 +333,8 @@ public class ConfigcenterContext { result = LocalCacheUtils.read(); } else { LocalCacheUtils.write(result); + int configCount = result.size(); + logger.info("[ConfigClient] fetched {} configs", configCount); } return result; } @@ -410,7 +412,7 @@ public class ConfigcenterContext { // Refresh @Value and @ConfigurationProperties fields logger.info("[RefreshScope] Calling refreshDynamicConfigFields()..."); - refreshDynamicConfigFields(); + refreshDynamicConfigFields(updateConfig.keySet()); logger.info("[RefreshScope] refreshDynamicConfigFields() completed"); } } @@ -448,13 +450,13 @@ public class ConfigcenterContext { } } // Refresh @Value and @ConfigurationProperties fields - refreshDynamicConfigFields(); + refreshDynamicConfigFields(new HashSet<>(keys)); } - private void refreshDynamicConfigFields() { + private void refreshDynamicConfigFields(Set changedKeys) { if (applicationContext != null) { try { - logger.info("[RefreshScope] Starting dynamic config field refresh..."); + logger.info("[RefreshScope] Starting dynamic config field refresh for keys: {}", String.join(", ", changedKeys)); StandardEnvironment env = (StandardEnvironment) applicationContext.getEnvironment(); // 直接创建 RefreshScopeRegistry 并注册为 BeanPostProcessor RefreshScopeRegistry registry = new RefreshScopeRegistry(env); @@ -474,8 +476,8 @@ public class ConfigcenterContext { configPropsCount = registry.getConfigPropsCount(); logger.info("[RefreshScope] Recorded {} @Value fields and {} @ConfigurationProperties beans", valueFieldCount, configPropsCount); - // 刷新 - registry.refresh(); + // 刷新(只刷新受影响的字段) + registry.refresh(changedKeys); logger.info("[RefreshScope] RefreshScopeRegistry.refresh() completed, refreshed {} @Value fields and {} @ConfigurationProperties beans", registry.getValueFieldCount(), registry.getConfigPropsCount()); } catch (Exception e) { logger.warn("[RefreshScope] RefreshScopeRegistry.refresh() error, msg:{}", e.getMessage(), e); diff --git a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/InternalConfigChangeListener.java b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/InternalConfigChangeListener.java index a35d670..73d80c6 100644 --- a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/InternalConfigChangeListener.java +++ b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/InternalConfigChangeListener.java @@ -72,7 +72,10 @@ public class InternalConfigChangeListener { logger.info("[ConfigClient] {} configs deleted: {}", deletedKeys.length, String.join(", ", deletedKeys)); } } - if (!data.isEmpty()) context.updateConfig(data); + if (!data.isEmpty()) { + context.updateConfig(data); + logger.info("[ConfigClient] {} configs changed: {}", data.size(), String.join(", ", data.keySet())); + } } } } catch (Exception e) { diff --git a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/RefreshScopeRegistry.java b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/RefreshScopeRegistry.java index c89cfea..79bf5ab 100644 --- a/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/RefreshScopeRegistry.java +++ b/kenaito-conf-springboot2-client/src/main/java/cn/odboy/confcenter/RefreshScopeRegistry.java @@ -3,6 +3,7 @@ package cn.odboy.confcenter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,27 +61,29 @@ public class RefreshScopeRegistry implements BeanPostProcessor { } /** - * Refresh all @Value fields and @ConfigurationProperties beans + * Refresh only @Value fields and @ConfigurationProperties beans that are affected by changed keys */ - public void refresh() { - refreshValueFields(); - refreshConfigPropsBeans(); + public void refresh(Set changedKeys) { + refreshValueFields(changedKeys); + refreshConfigPropsBeans(changedKeys); } - private void refreshValueFields() { + private void refreshValueFields(Set changedKeys) { for (ValueFieldHolder holder : valueFields) { - try { - holder.refresh(environment); - } catch (Exception e) { - // ignore refresh errors + if (changedKeys.contains(holder.key)) { + try { + holder.refresh(environment); + } catch (Exception e) { + // ignore refresh errors + } } } } - private void refreshConfigPropsBeans() { + private void refreshConfigPropsBeans(Set changedKeys) { for (ConfigPropsHolder holder : configPropsBeans) { try { - holder.refresh(environment); + holder.refresh(environment, changedKeys); } catch (Exception e) { // ignore refresh errors } @@ -149,24 +152,27 @@ public class RefreshScopeRegistry implements BeanPostProcessor { this.bean = bean; } - void refresh(Environment env) { + void refresh(Environment env, Set changedKeys) { Class clazz = bean.getClass(); ConfigurationProperties anno = clazz.getAnnotation(ConfigurationProperties.class); if (anno != null) { String prefix = anno.prefix(); - bindProperties(bean, prefix, env); + bindProperties(bean, prefix, env, changedKeys); } } - private void bindProperties(Object bean, String prefix, Environment env) { + private void bindProperties(Object bean, String prefix, Environment env, Set changedKeys) { try { Class clazz = bean.getClass(); for (Field field : getAllDeclaredFields(clazz)) { String propertyName = prefix + "." + camelToKebab(field.getName()); - String value = env.getProperty(propertyName); - if (value != null) { - field.setAccessible(true); - field.set(bean, convertType(field.getType(), value)); + // Only refresh if this property key is in changedKeys + if (changedKeys.contains(propertyName)) { + String value = env.getProperty(propertyName); + if (value != null) { + field.setAccessible(true); + field.set(bean, convertType(field.getType(), value)); + } } } } catch (Exception e) {