From 4b171d523349ec90082cab1a889db04225fcfca7 Mon Sep 17 00:00:00 2001 From: canonical Date: Sun, 11 Aug 2024 19:49:28 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4profile=E7=9A=84=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=80=BB=E8=BE=91=EF=BC=8C=E5=85=81=E8=AE=B8=E5=9C=A8?= =?UTF-8?q?application.yaml=E4=B8=AD=E9=85=8D=E7=BD=AEnop.profile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/dev-guide/config.md | 28 ++++--- .../main/java/io/nop/api/core/ApiConfigs.java | 6 +- .../config/source/ProfileConfigSource.java | 3 +- .../io/nop/config/starter/ConfigStarter.java | 75 ++++++++++++------- 4 files changed, 72 insertions(+), 40 deletions(-) diff --git a/docs/dev-guide/config.md b/docs/dev-guide/config.md index 7e432d8f8..ed398d095 100644 --- a/docs/dev-guide/config.md +++ b/docs/dev-guide/config.md @@ -11,17 +11,19 @@ 先加载的配置优先级更高,会被优先使用。 -1. classpath:bootstrap.yaml 应用的启动配置,其中的所有变量都是固定值,不会被动态覆盖 -2. 配置中心的`{nop.application.name}-{nop.profile}.yaml` -3. 配置中心的`{nop.application.name}.yaml` -4. nop.config.key-config-source.paths参数指定k8s SecretMap映射文件,定时扫描检测是否已更新 -5. nop.config.props-config-source.paths参数指定k8s ConfigMap映射文件,定时扫描检测是否已更新 -6. 如果配置了nop.config.jdbc.jdbc-url等参数,则会从数据库配置表中加载配置,定时扫描检测是否已更新 -7. java的System.getProperties() -8. java的System.getenv(): StringHelper.envToConfigVar(envName)负责把环境变量名转换为配置项名称 -9. nop.config.additional-location参数指定的配置文件 -10. nop.config.location参数指定的配置文件,它的缺省值为 classpath:application.yaml -11. 识别quarkus配置规范规定的`'%dev.'`等profile配置前缀,根据当前的profile配置调整专属于profile的配置项的访问顺序。例如 +1. java的System.getenv(): StringHelper.envToConfigVar(envName)负责把环境变量名转换为配置项名称 +2. java的System.getProperties() +3. classpath:bootstrap.yaml 应用的启动配置, 可以通过`nop.config.bootstrap-location`来指定位置 +4. 配置中心的`{nop.application.name}-{nop.profile}.yaml` +5. 配置中心的`{nop.application.name}.yaml` +6. 配置中心的`{nop.product.name}.yaml` +7. `nop.config.key-config-source.paths`参数指定k8s SecretMap映射文件,定时扫描检测是否已更新 +8. `nop.config.props-config-source.paths`参数指定k8s ConfigMap映射文件,定时扫描检测是否已更新 +9. 如果配置了`nop.config.jdbc.jdbc-url`等参数,则会从数据库配置表中加载配置,定时扫描检测是否已更新 +10. `nop.config.additional-location`参数指定的扩展配置文件 +11. `nop.config.location`参数指定的配置文件,它的缺省值为`classpath:application.yaml` +12. `nop.profile`和`nop.profile.parent`指定的profile配置,它们的优先级高于application.yaml +13. 识别quarkus配置规范规定的`'%dev.'`等profile配置前缀,根据当前的profile配置调整专属于profile的配置项的访问顺序。例如 dev模式下,`%dev.a.b.c`的值将会覆盖配置项`a.b.c`的值 > @@ -29,9 +31,11 @@ 具体配置加载逻辑全部集中在[ConfigStarter.java](https://gitee.com/canonical-entropy/nop-entropy/blob/master/nop-config/src/main/java/io/nop/config/starter/ConfigStarter.java) 类中 -* **在bootstrap.yaml中可以配置nop.profile=dev来启用application-dev.yaml配置,类似于spring中的profile概念。** +* **在bootstrap.yaml或者application.yaml中可以配置nop.profile=dev来启用application-dev.yaml配置,类似于spring中的profile概念。** 也可以通过java property或者env机制类配置profile,例如-Dnop.profile=dev或者配置环境变量`NOP_PROFILE=dev` * 优先加载yaml后缀的配置文件,如果找不到,会尝试加载后缀名为yml的同名文件。也就是说,如果同时存在`application.yaml`和`application.yml`,则会优先使用前者 +* 可以通过`nop.profile.parent`来指定多个active的profile,它们的优先级为从左到右。例如`nop.profile=dev`, `nop.profile.parent=mysql,nacos`,则 +对应于如下加载顺序 `application-dev.yaml -> application-mysql.yaml -> application-nacos.yaml -> application.yaml`。 ## 自动更新 diff --git a/nop-api-core/src/main/java/io/nop/api/core/ApiConfigs.java b/nop-api-core/src/main/java/io/nop/api/core/ApiConfigs.java index b671a8225..1fe65f082 100644 --- a/nop-api-core/src/main/java/io/nop/api/core/ApiConfigs.java +++ b/nop-api-core/src/main/java/io/nop/api/core/ApiConfigs.java @@ -12,6 +12,8 @@ import io.nop.api.core.config.IConfigReference; import io.nop.api.core.util.SourceLocation; +import java.util.Set; + import static io.nop.api.core.config.AppConfig.varRef; @Locale("zh-CN") @@ -23,8 +25,8 @@ public interface ApiConfigs { varRef(s_loc, "nop.profile", String.class, null); @Description("当前profile的父配置。配置查找顺序为 profile --> profile.parent --> standard") - IConfigReference CFG_PROFILE_PARENT = - varRef(s_loc, "nop.profile.parent", String.class, null); + IConfigReference CFG_PROFILE_PARENT = + varRef(s_loc, "nop.profile.parent", Set.class, null); @Description("在NopException中记录的xpl堆栈的最大深度") IConfigReference CFG_EXCEPTION_MAX_XPL_STACK_SIZE = diff --git a/nop-config/src/main/java/io/nop/config/source/ProfileConfigSource.java b/nop-config/src/main/java/io/nop/config/source/ProfileConfigSource.java index 85eb98c07..9edbe0601 100644 --- a/nop-config/src/main/java/io/nop/config/source/ProfileConfigSource.java +++ b/nop-config/src/main/java/io/nop/config/source/ProfileConfigSource.java @@ -7,6 +7,7 @@ */ package io.nop.config.source; +import io.nop.commons.util.CollectionHelper; import io.nop.commons.util.objects.ValueWithLocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +28,7 @@ public class ProfileConfigSource implements IConfigSource { private final IConfigSource source; public ProfileConfigSource(List profiles, IConfigSource source) { - this.profiles = profiles; + this.profiles = CollectionHelper.reverseList(profiles); this.source = source; } diff --git a/nop-config/src/main/java/io/nop/config/starter/ConfigStarter.java b/nop-config/src/main/java/io/nop/config/starter/ConfigStarter.java index a8a6bc94d..8c25b944f 100644 --- a/nop-config/src/main/java/io/nop/config/starter/ConfigStarter.java +++ b/nop-config/src/main/java/io/nop/config/starter/ConfigStarter.java @@ -7,7 +7,6 @@ */ package io.nop.config.starter; -import io.nop.api.core.ApiConfigs; import io.nop.api.core.config.AppConfig; import io.nop.api.core.config.IConfigExecutor; import io.nop.api.core.config.IConfigProvider; @@ -61,7 +60,10 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; +import static io.nop.api.core.ApiConfigs.CFG_PROFILE; +import static io.nop.api.core.ApiConfigs.CFG_PROFILE_PARENT; import static io.nop.config.ConfigConstants.CFG_KEY_FILE_CONFIG_SOURCE_PATHS; import static io.nop.config.ConfigConstants.CFG_KEY_FILE_CONFIG_SOURCE_REFRESH_INTERVAL; import static io.nop.config.ConfigConstants.CFG_PROPS_FILE_CONFIG_SOURCE_PATHS; @@ -90,6 +92,8 @@ public static ConfigStarter instance() { private DefaultVirtualFileSystem vfs; + private List profiles; + private Cancellable cancellable = new Cancellable(); private List closeableSources = new ArrayList<>(); @@ -99,11 +103,11 @@ protected void doStart() { // 1. 装载boostrap.yaml IConfigSource propsSource = new SysPropertyConfigSourceLoader().loadConfigSource(null); IConfigSource envSource = new EnvConfigSourceLoader().loadConfigSource(null); - IConfigSource bootstrapSource = loadBootstrapSource(propsSource, envSource); + IConfigSource bootstrapSource = loadBootstrapSource(); - // 优先级从高到低 System.properties --> bootstrap.yaml --> System.env + // 优先级从高到低 System.env --> System.properties --> bootstrap.yaml, 与spring保持一致 CompositeConfigSource baseSource = new CompositeConfigSource( - Arrays.asList(propsSource, bootstrapSource, envSource)); + Arrays.asList(envSource, propsSource, bootstrapSource)); initConfigProvider(baseSource); @@ -174,11 +178,16 @@ protected void doStart() { configSource = new CompositeConfigSource(configSources); // 8. 装载应用配置 application.yaml - List appSources = loadAppConfigs(configSource); + IConfigSource appSource = loadAppConfigSource(configSource); + + List appSources = loadAppConfigs(configSource, appSource); configSources.addAll(appSources); - List profiles = getProfiles(configSource); + List profiles = getProfiles(configSource, appSource); + + LOG.info("nop.profiles.active:{}", profiles); + configSource = new CompositeConfigSource(configSources); if (!profiles.isEmpty()) { configSource = new ProfileConfigSource(profiles, configSource); @@ -215,7 +224,7 @@ public void deactivate() { this.changeApplier.deactivate(); } - private IConfigSource loadBootstrapSource(IConfigSource propsSource, IConfigSource envSource) { + private IConfigSource loadBootstrapSource() { IResource resource = CoreInitialization.getBootstrapResource(); if (!resource.exists()) { LOG.warn("nop.config.no-bootstrap-config-file:path{}", resource.getPath()); @@ -251,11 +260,19 @@ protected IResource buildConfigResource(String path) { return ResourceHelper.buildConfigResource(path); } - protected List getProfiles(IConfigSource baseSource) { - String profile = baseSource.getConfigValue(ApiConfigs.CFG_PROFILE.getName(), ""); + protected List getProfiles(IConfigSource baseSource, IConfigSource appSource) { + if (this.profiles != null) + return this.profiles; + + String profile = appSource.getConfigValue(CFG_PROFILE.get(), null); + if (StringHelper.isEmpty(profile)) + profile = baseSource.getConfigValue(CFG_PROFILE.getName(), ""); - String profileParent = baseSource.getConfigValue(ApiConfigs.CFG_PROFILE_PARENT.getName(), ""); - if (StringHelper.isEmpty(profile) && StringHelper.isEmpty(profileParent)) + Set profileParent = ConvertHelper.toCsvSet(appSource.getConfigValue(CFG_PROFILE_PARENT.getName())); + if (CollectionHelper.isEmpty(profileParent)) + profileParent = ConvertHelper.toCsvSet(baseSource.getConfigValue(CFG_PROFILE_PARENT.getName())); + + if (StringHelper.isEmpty(profile) && CollectionHelper.isEmpty(profileParent)) return Collections.emptyList(); List profiles = new ArrayList<>(); @@ -263,8 +280,10 @@ protected List getProfiles(IConfigSource baseSource) { profiles.add(profile); } - if (!StringHelper.isEmpty(profileParent)) - profiles.add(profileParent); + if (profileParent != null) + profiles.addAll(profileParent); + + this.profiles = profiles; return profiles; } @@ -272,8 +291,6 @@ protected List loadFromConfigCenter(IConfigSource baseSource) { if (configService == null) return Collections.emptyList(); - String productName = baseSource.getConfigValue(ConfigConstants.CFG_PRODUCT_NAME, ""); - String appName = baseSource.getConfigValue(ConfigConstants.CFG_APPLICATION_NAME, ""); if (StringHelper.isEmpty(appName)) throw new NopException(ERR_CONFIG_MISSING_APPLICATION_NAME); @@ -281,18 +298,20 @@ protected List loadFromConfigCenter(IConfigSource baseSource) { List serviceSources = new ArrayList<>(2); try { - List profiles = getProfiles(baseSource); + IConfigSource appSource = configService.getConfigSource(baseSource, appName); + + List profiles = getProfiles(baseSource, appSource); for (String profile : profiles) { String dataId = appName + '-' + profile; IConfigSource profileSource = configService.getConfigSource(baseSource, dataId); serviceSources.add(profileSource); } - if (!StringHelper.isEmpty(appName)) { - String dataId = appName; - IConfigSource serviceSource = configService.getConfigSource(baseSource, dataId); - serviceSources.add(serviceSource); - } + serviceSources.add(appSource); + + String productName = appSource.getConfigValue(ConfigConstants.CFG_PRODUCT_NAME, ""); + if (StringHelper.isEmpty(productName)) + productName = baseSource.getConfigValue(ConfigConstants.CFG_PRODUCT_NAME, ""); if (!StringHelper.isEmpty(productName)) { String dataId = productName; @@ -340,7 +359,7 @@ protected IConfigSource loadJdbcSource(IConfigSource configSource) { return new JdbcConfigSource(config); } - protected List loadAppConfigs(IConfigSource configSource) { + protected List loadAppConfigs(IConfigSource configSource, IConfigSource appSource) { List appSources = new ArrayList<>(4); String additional = configSource.getConfigValue(ConfigConstants.CFG_CONFIG_ADDITIONAL_LOCATION, ""); @@ -350,15 +369,22 @@ protected List loadAppConfigs(IConfigSource configSource) { appSources.add(source); } - List profiles = getProfiles(configSource); + List profiles = getProfiles(configSource, appSource); for (String profile : profiles) { IResource resource = getAppProfileFile(profile); if (resource.exists()) { IConfigSource source = new ResourceConfigSourceLoader(resource).loadConfigSource(configSource); appSources.add(source); + } else { + LOG.warn("nop.profile.not-exists:{}", profile); } } + appSources.add(appSource); + return appSources; + } + + protected IConfigSource loadAppConfigSource(IConfigSource configSource) { String path = configSource.getConfigValue(ConfigConstants.CFG_CONFIG_LOCATION, ConfigConstants.CFG_PATH_APPLICATION_YAML); IResource resource = buildConfigResource(path); @@ -369,8 +395,7 @@ protected List loadAppConfigs(IConfigSource configSource) { } } IConfigSource source = new ResourceConfigSourceLoader(resource).loadConfigSource(configSource); - appSources.add(source); - return appSources; + return source; } protected IResource getAppProfileFile(String profile) {