14-Dubbo配置加载全解析

    初始化过程中会先启动配置中心配置信息处理,然后 调用加载初始化应用程序配置方法loadApplicationConfigs();进行配置加载
    关于配置的官方文档链接为

    Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。

    常用配置组件如下

    • application: Dubbo应用配置
    • registry: 注册中心
    • protocol: 服务提供者RPC协议
    • config-center: 配置中心
    • metadata-report: 元数据中心
    • service: 服务提供者配置
    • reference: 远程服务引用配置
    • provider: service的默认配置或分组配置
    • consumer: reference的默认配置或分组配置
    • module: 模块配置
    • monitor: 监控配置
    • metrics: 指标配置
    • ssl: SSL/TLS配置

    配置还有几个比较重要的点:

    配置来源
    从Dubbo支持的配置来源说起,默认有6种配置来源:

    • JVM System Properties,JVM -D 参数
    • System environment,JVM进程的环境变量
    • Externalized Configuration,外部化配置,从配置中心读取
    • Application Configuration,应用的属性配置,从Spring应用的Environment中提取”dubbo”打头的属性集
    • API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式
    • 从classpath读取配置文件 dubbo.properties

    覆盖关系
    下图展示了配置覆盖关系的优先级,从上到下优先级依次降低:

    配置方式

    • Java API配置
    • XML配置
    • Annotation配置
    • 属性配置

    配置虽然非常多,但是我们掌握一下配置加载的原理,再了解下官网的文档说明路径应该基础的配置搞定是没问题的,更深入的配置很多参数还是需要了解下源码的.

    前面我们在讲ModuleModel对象的创建的时候ModuleModel模型中包含了一个成员变量为ModuleEnvironment 代表当前的模块环境和ModuleConfigManager配置管理器
    而ModuleModel模型对象的父模型对象ApplicationModel中包含了一个成员变量Environment环境和ConfigManager配置管理器.

    在回顾调用过程之前我们先看下模型,配置管理器和环境与配置之间的关系如下图:
    在这里插入图片描述

    1. protected void initialize() {
    2. super.initialize();
    3. this.serviceRepository = new ModuleServiceRepository(this);
    4. this.moduleConfigManager = new ModuleConfigManager(this);
    5. this.moduleConfigManager.initialize();

    ModuleEnvironment环境信息对象也会在配置管理器创建的时候被调用到:
    如下代码所示:

    1. @Override
    2. public ModuleEnvironment getModelEnvironment() {
    3. if (moduleEnvironment == null) {
    4. moduleEnvironment = (ModuleEnvironment) this.getExtensionLoader(ModuleExt.class)
    5. .getExtension(ModuleEnvironment.NAME);
    6. }
    7. return moduleEnvironment;
    8. }

    在扩展对象ExtensionLoader进行对象ModuleEnvironment创建之后会对对象进行初始化调用 initExtension(instance)方法 初始化的时候调用如下代码:
    ExtensionLoader中的初始化方法如下:

    1. private void initExtension(T instance) {
    2. if (instance instanceof Lifecycle) {
    3. Lifecycle lifecycle = (Lifecycle) instance;
    4. lifecycle.initialize();
    5. }
    6. }

    这个初始化方法对应ModuleEnvironment的父类型Environment中的初始化方法如下:initialize()

    1. @Override
    2. public void initialize() throws IllegalStateException {
    3. if (initialized.compareAndSet(false, true)) {
    4. //加载在JVM或者环境变量指定的dubbo.properties配置文件 配置的key为dubbo.properties.file ,如果未指定则查找类路径下的dubbo.properties
    5. this.propertiesConfiguration = new PropertiesConfiguration(scopeModel);
    6. //系统JVM参数的配置无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getProperty(key)来调用
    7. this.systemConfiguration = new SystemConfiguration();
    8. //系统环境变量的配置,无需我们来加载到内存,系统已经加载好了放到了System中,我们只需System.getenv(key)来获取就可以
    9. this.environmentConfiguration = new EnvironmentConfiguration();
    10. //从远程配置中心的全局配置获取对应配置
    11. this.externalConfiguration = new InmemoryConfiguration("ExternalConfig");
    12. //从远程配置中心的应用配置获取对应配置
    13. this.appExternalConfiguration = new InmemoryConfiguration("AppExternalConfig");
    14. //应用内的配置比如: Spring Environment/PropertySources/application.properties
    15. this.appConfiguration = new InmemoryConfiguration("AppConfig");
    16. //加载迁移配置,用户在JVM参数或者环境变量中指定的dubbo.migration.file,如果用户未指定测尝试加载类路径下的dubbo-migration.yaml
    17. loadMigrationRule();
    18. }
    19. }

    14.4.2 属性变量说明

    前面我们已经基本上介绍了各个属性的含义下面用一个表格列举一下方便查看:

    关于每个配置信息这里还是来了解下细节,方便大家了解原理.

    如前面所示:

    下面就直接提构造器的PropertiesConfiguration代码了:

    1. public PropertiesConfiguration(ScopeModel scopeModel) {
    2. this.scopeModel = scopeModel;
    3. refresh();
    4. }
    5. public void refresh() {
    6. //配置获取的过程是借助工具类ConfigUtils来获取的
    7. properties = ConfigUtils.getProperties(scopeModel.getClassLoaders());
    8. }

    继续看ConfigUtils的getProperties方法:

    1. public static Properties getProperties(Set<ClassLoader> classLoaders) {
    2. //这个配置的KEY是dubbo.properties.file System.getProperty是从JVM参数中获取配置的 一般情况下我们在启动Java进程的时候会指定Dubbo配置文件 如配置:
    3. //-Ddubbo.properties.file=/dubbo.properties
    4. String path = System.getProperty(CommonConstants.DUBBO_PROPERTIES_KEY);
    5. if (StringUtils.isEmpty(path)) {
    6. //优先级最高的JVM参数拿不到数据则从 环境变量中获取,这个配置key也是dubbo.properties.file System.getenv是从环境变量中获取数据
    7. //例如我们在环境变量中配置 dubbo.properties.file=/dubbo.properties
    8. path = System.getenv(CommonConstants.DUBBO_PROPERTIES_KEY);
    9. //如果在JVM参数和环境变量都拿不到这个配置文件的路径我们就用默认的吧
    10. //默认的路径是类路径下的资源文件 这个路径是: dubbo.properties
    11. path = CommonConstants.DEFAULT_DUBBO_PROPERTIES;
    12. }
    13. }
    14. return ConfigUtils.loadProperties(classLoaders, path, false, true);
    15. }

    路径获取之后加载详细的配置内容:

    ConfigUtils的loadProperties代码如下:

    1. ConfigUtils.loadProperties(classLoaders, path, false, true);
    1. public static Properties loadProperties(Set<ClassLoader> classLoaders, String fileName, boolean allowMultiFile, boolean optional) {
    2. Properties properties = new Properties();
    3. // add scene judgement in windows environment Fix 2557
    4. //检查文件是否存在 直接加载配置文件如果加载到了配置文件则直接返回
    5. if (checkFileNameExist(fileName)) {
    6. try {
    7. FileInputStream input = new FileInputStream(fileName);
    8. try {
    9. properties.load(input);
    10. } finally {
    11. input.close();
    12. } catch (Throwable e) {
    13. logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
    14. }
    15. return properties;
    16. }
    17. //为什么会有下面的逻辑呢,如果仅仅使用上面的加载方式只能加载到本系统下的配置文件,无法加载封装在jar中的根路径的配置
    18. Set<java.net.URL> set = null;
    19. try {
    20. List<ClassLoader> classLoadersToLoad = new LinkedList<>();
    21. classLoadersToLoad.add(ClassUtils.getClassLoader());
    22. classLoadersToLoad.addAll(classLoaders);
    23. //这个方法loadResources在扩展加载的时候说过
    24. set = ClassLoaderResourceLoader.loadResources(fileName, classLoadersToLoad).values().stream().reduce(new LinkedHashSet<>(), (a, i) -> {
    25. a.addAll(i);
    26. return a;
    27. });
    28. } catch (Throwable t) {
    29. logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
    30. }
    31. if (CollectionUtils.isEmpty(set)) {
    32. if (!optional) {
    33. logger.warn("No " + fileName + " found on the class path.");
    34. }
    35. return properties;
    36. }
    37. if (!allowMultiFile) {
    38. if (set.size() > 1) {
    39. String errMsg = String.format("only 1 %s file is expected, but %d dubbo.properties files found on class path: %s",
    40. fileName, set.size(), set);
    41. logger.warn(errMsg);
    42. }
    43. // fall back to use method getResourceAsStream
    44. try {
    45. properties.load(ClassUtils.getClassLoader().getResourceAsStream(fileName));
    46. } catch (Throwable e) {
    47. logger.warn("Failed to load " + fileName + " file from " + fileName + "(ignore this file): " + e.getMessage(), e);
    48. }
    49. return properties;
    50. }
    51. logger.info("load " + fileName + " properties file from " + set);
    52. for (java.net.URL url : set) {
    53. try {
    54. Properties p = new Properties();
    55. InputStream input = url.openStream();
    56. if (input != null) {
    57. try {
    58. p.load(input);
    59. properties.putAll(p);
    60. } finally {
    61. try {
    62. input.close();
    63. } catch (Throwable t) {
    64. }
    65. }
    66. }
    67. } catch (Throwable e) {
    68. logger.warn("Fail to load " + fileName + " file from " + url + "(ignore this file): " + e.getMessage(), e);
    69. }
    70. }
    71. return properties;
    72. }

    完整的配置加载流程这里用简单的话描述下:

    • 项目内配置查询
      • 路径查询
        • 从JVM参数中获取配置的 dubbo.properties.file配置文件路径
        • 如果前面未获取到路径则使用默认路径dubbo.propertie
      • 配置加载
        • 将路径转为FileInputStream 然后使用Properties加载
    • 依赖中的配置扫描查询
      • 使用类加载器扫描所有资源URL
      • url转InputStream 如 url.openStream() 然后使用Properties加载

    14.3.4 加载JVM参数的配置

    这里我们继续看SystemConfiguration配置的加载
    这个直接看下代码就可以了:

    这个类型仅仅是使用System.getProperty来获取JVM配置即可

    这里我们来看EnvironmentConfiguration,这里我们直接来看代码:

    1. public class EnvironmentConfiguration implements Configuration {
    2. @Override
    3. public Object getInternalProperty(String key) {
    4. String value = System.getenv(key);
    5. if (StringUtils.isEmpty(value)) {
    6. value = System.getenv(StringUtils.toOSStyleKey(key));
    7. }
    8. }
    9. public Map<String, String> getProperties() {
    10. return System.getenv();
    11. }
    12. }

    14.3.6 内存配置的封装:InmemoryConfiguration

    这里我们看下InmemoryConfiguration的设计,这个直接看代码吧内部使用了一个LinkedHashMap来存储配置

    1. public class InmemoryConfiguration implements Configuration {
    2. private String name;
    3. // stores the configuration key-value pairs
    4. private Map<String, String> store = new LinkedHashMap<>();
    5. public InmemoryConfiguration() {
    6. }
    7. public InmemoryConfiguration(String name) {
    8. this.name = name;
    9. }
    10. public InmemoryConfiguration(Map<String, String> properties) {
    11. this.setProperties(properties);
    12. }
    13. @Override
    14. public Object getInternalProperty(String key) {
    15. return store.get(key);
    16. }
    17. /**
    18. * Add one property into the store, the previous value will be replaced if the key exists
    19. */
    20. public void addProperty(String key, String value) {
    21. store.put(key, value);
    22. }
    23. /**
    24. * Add a set of properties into the store
    25. */
    26. public void addProperties(Map<String, String> properties) {
    27. if (properties != null) {
    28. this.store.putAll(properties);
    29. }
    30. }
    31. /**
    32. * set store
    33. */
    34. public void setProperties(Map<String, String> properties) {
    35. if (properties != null) {
    36. this.store = properties;
    37. }
    38. }
    39. public Map<String, String> getProperties() {
    40. return store;
    41. }
    42. }

    关于配置迁移文件的用法可以看下这个Dubbo官方的

    这个配置文件的文件名字为:dubbo-migration.yaml

    这个和14.3.4加载JVM参数配置的过程是相似的细节可以看14.3.4节

    1. private void loadMigrationRule() {
    2. //JVM参数的dubbo.migration.file配置
    3. String path = System.getProperty(CommonConstants.DUBBO_MIGRATION_KEY);
    4. if (StringUtils.isEmpty(path)) {
    5. //环境变量的dubbo.migration.file配置
    6. path = System.getenv(CommonConstants.DUBBO_MIGRATION_KEY);
    7. if (StringUtils.isEmpty(path)) {
    8. //默认的迁移配置文件 dubbo-migration.yaml
    9. path = CommonConstants.DEFAULT_DUBBO_MIGRATION_FILE;
    10. }
    11. }
    12. this.localMigrationRule = ConfigUtils.loadMigrationRule(scopeModel.getClassLoaders(), path);
    13. }

    加载配置涉及到了配置优先级的处理,

    下面来看加载配置代码 loadApplicationConfigs()方法

    1. private void loadApplicationConfigs() {
    2. //发布器还是不处理配置加载的逻辑还是交给配置管理器
    3. configManager.loadConfigs();
    4. }

    技术咨询支持,可以扫描微信公众号进行回复咨询