3.1 处理配置

Dubbo 提供了丰富的配置,用于调整和优化框架行为,性能等。Dubbo 在引用或导出服务时,首先会对这些配置进行检查和处理,以保证配置的正确性。配置解析逻辑封装在 ReferenceConfig 的 init 方法中,下面进行分析。

  1. private void init() {
  2. // 避免重复初始化
  3. if (initialized) {
  4. return;
  5. }
  6. initialized = true;
  7. // 检测接口名合法性
  8. if (interfaceName == null || interfaceName.length() == 0) {
  9. throw new IllegalStateException("interface not allow null!");
  10. }
  11. // 检测 consumer 变量是否为空,为空则创建
  12. checkDefault();
  13. appendProperties(this);
  14. if (getGeneric() == null && getConsumer() != null) {
  15. // 设置 generic
  16. setGeneric(getConsumer().getGeneric());
  17. }
  18. // 检测是否为泛化接口
  19. if (ProtocolUtils.isGeneric(getGeneric())) {
  20. interfaceClass = GenericService.class;
  21. } else {
  22. try {
  23. // 加载类
  24. interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
  25. .getContextClassLoader());
  26. } catch (ClassNotFoundException e) {
  27. throw new IllegalStateException(e.getMessage(), e);
  28. }
  29. checkInterfaceAndMethods(interfaceClass, methods);
  30. }
  31. // -------------------------------✨ 分割线1 ✨------------------------------
  32. // 从系统变量中获取与接口名对应的属性值
  33. String resolve = System.getProperty(interfaceName);
  34. String resolveFile = null;
  35. if (resolve == null || resolve.length() == 0) {
  36. // 从系统属性中获取解析文件路径
  37. resolveFile = System.getProperty("dubbo.resolve.file");
  38. if (resolveFile == null || resolveFile.length() == 0) {
  39. // 从指定位置加载配置文件
  40. File userResolveFile = new File(new File(System.getProperty("user.home")), "dubbo-resolve.properties");
  41. if (userResolveFile.exists()) {
  42. // 获取文件绝对路径
  43. resolveFile = userResolveFile.getAbsolutePath();
  44. }
  45. }
  46. if (resolveFile != null && resolveFile.length() > 0) {
  47. Properties properties = new Properties();
  48. FileInputStream fis = null;
  49. try {
  50. fis = new FileInputStream(new File(resolveFile));
  51. // 从文件中加载配置
  52. properties.load(fis);
  53. } catch (IOException e) {
  54. throw new IllegalStateException("Unload ..., cause:...");
  55. } finally {
  56. try {
  57. if (null != fis) fis.close();
  58. } catch (IOException e) {
  59. logger.warn(e.getMessage(), e);
  60. }
  61. }
  62. // 获取与接口名对应的配置
  63. resolve = properties.getProperty(interfaceName);
  64. }
  65. }
  66. if (resolve != null && resolve.length() > 0) {
  67. // 将 resolve 赋值给 url
  68. url = resolve;
  69. }
  70. // -------------------------------✨ 分割线2 ✨------------------------------
  71. if (consumer != null) {
  72. if (application == null) {
  73. // 从 consumer 中获取 Application 实例,下同
  74. application = consumer.getApplication();
  75. }
  76. if (module == null) {
  77. module = consumer.getModule();
  78. }
  79. if (registries == null) {
  80. registries = consumer.getRegistries();
  81. }
  82. if (monitor == null) {
  83. monitor = consumer.getMonitor();
  84. }
  85. }
  86. if (module != null) {
  87. if (registries == null) {
  88. registries = module.getRegistries();
  89. }
  90. if (monitor == null) {
  91. monitor = module.getMonitor();
  92. }
  93. }
  94. if (application != null) {
  95. if (registries == null) {
  96. registries = application.getRegistries();
  97. }
  98. if (monitor == null) {
  99. monitor = application.getMonitor();
  100. }
  101. }
  102. // 检测 Application 合法性
  103. checkApplication();
  104. // 检测本地存根配置合法性
  105. checkStubAndMock(interfaceClass);
  106. // -------------------------------✨ 分割线3 ✨------------------------------
  107. Map<String, String> map = new HashMap<String, String>();
  108. Map<Object, Object> attributes = new HashMap<Object, Object>();
  109. // 添加 side、协议版本信息、时间戳和进程号等信息到 map 中
  110. map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
  111. map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
  112. map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
  113. if (ConfigUtils.getPid() > 0) {
  114. map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
  115. }
  116. // 非泛化服务
  117. if (!isGeneric()) {
  118. // 获取版本
  119. String revision = Version.getVersion(interfaceClass, version);
  120. if (revision != null && revision.length() > 0) {
  121. map.put("revision", revision);
  122. }
  123. // 获取接口方法列表,并添加到 map 中
  124. String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
  125. if (methods.length == 0) {
  126. map.put("methods", Constants.ANY_VALUE);
  127. } else {
  128. map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
  129. }
  130. }
  131. map.put(Constants.INTERFACE_KEY, interfaceName);
  132. // 将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中
  133. appendParameters(map, application);
  134. appendParameters(map, module);
  135. appendParameters(map, consumer, Constants.DEFAULT_KEY);
  136. appendParameters(map, this);
  137. // -------------------------------✨ 分割线4 ✨------------------------------
  138. String prefix = StringUtils.getServiceKey(map);
  139. if (methods != null && !methods.isEmpty()) {
  140. // 遍历 MethodConfig 列表
  141. for (MethodConfig method : methods) {
  142. appendParameters(map, method, method.getName());
  143. String retryKey = method.getName() + ".retry";
  144. // 检测 map 是否包含 methodName.retry
  145. if (map.containsKey(retryKey)) {
  146. String retryValue = map.remove(retryKey);
  147. if ("false".equals(retryValue)) {
  148. // 添加重试次数配置 methodName.retries
  149. map.put(method.getName() + ".retries", "0");
  150. }
  151. }
  152. // 添加 MethodConfig 中的“属性”字段到 attributes
  153. // 比如 onreturn、onthrow、oninvoke 等
  154. appendAttributes(attributes, method, prefix + "." + method.getName());
  155. checkAndConvertImplicitConfig(method, map, attributes);
  156. }
  157. }
  158. // -------------------------------✨ 分割线5 ✨------------------------------
  159. // 获取服务消费者 ip 地址
  160. String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
  161. if (hostToRegistry == null || hostToRegistry.length() == 0) {
  162. hostToRegistry = NetUtils.getLocalHost();
  163. } else if (isInvalidLocalHost(hostToRegistry)) {
  164. throw new IllegalArgumentException("Specified invalid registry ip from property..." );
  165. }
  166. map.put(Constants.REGISTER_IP_KEY, hostToRegistry);
  167. // 存储 attributes 到系统上下文中
  168. StaticContext.getSystemContext().putAll(attributes);
  169. // 创建代理类
  170. ref = createProxy(map);
  171. // 根据服务名,ReferenceConfig,代理类构建 ConsumerModel,
  172. // 并将 ConsumerModel 存入到 ApplicationModel 中
  173. ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
  174. ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
  175. }

上面的代码很长,做的事情比较多。这里根据代码逻辑,对代码进行了分块,下面我们一起来看一下。

首先是方法开始到分割线1之间的代码。这段代码主要用于检测 ConsumerConfig 实例是否存在,如不存在则创建一个新的实例,然后通过系统变量或 dubbo.properties 配置文件填充 ConsumerConfig 的字段。接着是检测泛化配置,并根据配置设置 interfaceClass 的值。接着来看分割线1到分割线2之间的逻辑。这段逻辑用于从系统属性或配置文件中加载与接口名相对应的配置,并将解析结果赋值给 url 字段。url 字段的作用一般是用于点对点调用。继续向下看,分割线2和分割线3之间的代码用于检测几个核心配置类是否为空,为空则尝试从其他配置类中获取。分割线3与分割线4之间的代码主要用于收集各种配置,并将配置存储到 map 中。分割线4和分割线5之间的代码用于处理 MethodConfig 实例。该实例包含了事件通知配置,比如 onreturn、onthrow、oninvoke 等。分割线5到方法结尾的代码主要用于解析服务消费者 ip,以及调用 createProxy 创建代理对象。关于该方法的详细分析,将会在接下来的章节中展开。