本文实际是 《Dubbo 源码分析 —— 集成 Spring Boot》文章。考虑到和 Dubbo 配置比较相关,所以改成这个标题。
1. 概述
本文,我们来分享 https://github.com/apache/incubator-dubbo-spring-boot-project 项目的源码解析,看看 Dubbo 是如何集成到 Spring Boot 中的。
在阅读本文之前,希望胖友能够先熟读 中文文档 。最好呢,当然不强制,可以操练下每个 Demo 。
2. 调试环境搭建
在读源码之前,我们当然是先把调试环境搭建起来。
2.1 依赖工具
- JDK :1.8+
- Maven
- IntelliJ IDEA
2.2 源码拉取
从官方仓库 https://github.com/apache/incubator-dubbo-spring-boot-project Fork
出属于自己的仓库。为什么要 Fork
?既然开始阅读、调试源码,我们可能会写一些注释,有了自己的仓库,可以进行自由的提交。😈
使用 IntelliJ IDEA 从 Fork 出来的仓库拉取代码。拉取完成后,Maven 会下载依赖包,可能会花费一些时间,耐心等待下。
在等待的过程中,我来简单说下,搭建调试环境的过程:
- 启动 Dubbo Provider
- 启动 Dubbo Consumer
考虑到方便,我们直接使用 dubbo-registry-zookeeper-samples
项目提供的示例。
😈 另外,本文使用的 incubator-dubbo-spring-boot-project
版本是 0.2.1
。
2.3 启动 Dubbo Provider
右键运行 dubbo-registry-zookeeper-samples
项目下的 provider-sample
的 DubboRegistryZooKeeperProviderBootstrap 的 #main(String[] args)
方法,Provider 就启动完成了。输出日志如下图:日志
这个示例比较有意思的是,提供了 EmbeddedZooKeeper 类,用于启动内嵌的 Zookeeper 。
2.4 启动 Dubbo Consumer
右键运行 dubbo-registry-zookeeper-samples
项目下的 consumer-sample
的 DubboRegistryZooKeeperConsumerBootstrap 的 #main(String[] args)
方法,Consumer 就启动完成了。输出日志如下图:日志
因为 DubboRegistryZooKeeperConsumerBootstrap 的 Spring Boot 启动调用如下:
// DubboRegistryZooKeeperConsumerBootstrap.java |
<X>
处,所以在发起一次 Dubbo 调用之后,会直接关闭 Spring Boot 应用。因此,JVM 进程就直接结束了。
3. 项目结构一览
本文主要分享 incubator-dubbo-spring-boot-project
的 项目结构。
希望通过本文能让胖友对 incubator-dubbo-spring-boot-project
的整体项目有个简单的了解。
3.1 代码统计
这里先分享一个小技巧。笔者在开始源码学习时,会首先了解项目的代码量。
第一种方式,使用 IDEA Statistic 插件,统计整体代码量。
我们可以粗略的看到,总的代码量在 2000 行。这其中还包括单元测试,示例等等代码。
所以,不慌,一点不慌~
第二种方式,使用 Shell 脚本命令逐个 Maven 模块统计 。
一般情况下,笔者使用 find . -name "*.java"|xargs cat|grep -v -e ^$ -e ^\s*\/\/.*$|wc -l
。这个命令只过滤了部分注释,所以相比 IDEA Statistic 会偏多。
当然,考虑到准确性,胖友需要手动 cd
到每个 Maven 项目的 src/main/java
目录下,以达到排除单元测试的代码量。
统计完后,是不是更加不慌了。哈哈哈哈。
3.2 dubbo-spring-boot-parent 模块
dubbo-spring-boot-parent
模块,无具体代码,作为其它项目的 Maven Parent 项目,例如定义了依赖版本号。
3.3 dubbo-spring-boot-starter 模块
dubbo-spring-boot-starter
模块,无具体代码,作为 Spring Boot Dubbo Starter 模块。其 pom.xml
的代码如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" |
3.4 dubbo-spring-boot-distribution 模块
dubbo-spring-boot-distribution
模块,无具体代码,用于 Spring Boot Dubbo 使用 maven-assembly-plugin
插件,打包出我们后续使用的 Releases 。
3.5 dubbo-spring-boot-autoconfigure 模块
dubbo-spring-boot-autoconfigure
模块,754 行代码,提供了 Spring Boot Dubbo 的自动配置(AutoConfigure)的具体实现。
3.6 dubbo-spring-boot-actuator 模块
dubbo-spring-boot-actuator
模块,782 行代码,提供了 Spring Boot Dubbo 的 Actuator 的具体实现。
Spring Boot Actuator 的关键特性,是在应用程序里提供众多 HTTP 接口,通过它们了解应用程序运行时的内部状况。
3.7 dubbo-spring-boot-samples 模块
dubbo-spring-boot-samples
模块,708 行代码,提供了四个示例。比较重点可以看的是两个:
dubbo-registry-zookeeper-samples
模块,提供基于 Zookeeper 作为注册中心的使用示例。externalized-configuration-samples
模块,提供了外部化配置的示例。
3.8 小结
貌似也没啥好小结的。想要偷懒的话,只要看 dubbo-spring-boot-autoconfigure
模块,一共是 754 行代码。哈哈哈~
当然,真的要深入的话,还是要看 Dubbo 本身的代码的。
4. dubbo-spring-boot-autoconfigure
源码
dubbo-spring-boot-autoconfigure
模块,所有类如下图:`dubbo-spring-boot-autoconfigure` 模块
如下开始,是
autoconfigure
包下。
4.1 DubboAutoConfiguration
com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration
,Dubbo 自动配置类。代码如下:
// DubboAutoConfiguration.java |
- 类上的每个注解的作用,请看其后的注释。
4.1.1 serviceAnnotationBeanPostProcessor
#serviceAnnotationBeanPostProcessor()
方法,创建 ServiceAnnotationBeanPostProcessor Bean 对象。代码如下:
// DubboAutoConfiguration.java |
<1>
处,获得"dubbo.scan.base-package"
属性,即要扫描 Dubbo 注解的包。<2>
处,创建 ServiceAnnotationBeanPostProcessor 对象。后续,ServiceAnnotationBeanPostProcessor 会扫描packagesToScan
包的 Dubbo@Service
注解,创建对应的 Dubbo Service Bean 对象们。- ServiceAnnotationBeanPostProcessor 属于 Dubbo 项目本身,所以本文就不解析逻。
4.1.2 referenceAnnotationBeanPostProcessor
#referenceAnnotationBeanPostProcessor()
方法,创建 Bean 名字为 "referenceAnnotationBeanPostProcessor"
的 ReferenceAnnotationBeanPostProcessor Bean 对象。代码如下:
// DubboAutoConfiguration.java |
- 后续,ReferenceAnnotationBeanPostProcessor 会扫描 Dubbo
@Reference
注解,创建对应的 Dubbo Service Bean 对象们。- ReferenceAnnotationBeanPostProcessor 属于 Dubbo 项目本身,所以本文就不解析逻。
4.1.3 relaxedDubboConfigBinder
#relaxedDubboConfigBinder()
方法,创建 RelaxedDubboConfigBinder Bean 对象。代码如下:
// DubboAutoConfiguration.java |
- RelaxedDubboConfigBinder ,用于将具体的属性,设置到相应的 AbstractConfig 对象中。
- 为什么
@Scope(scopeName = SCOPE_PROTOTYPE)
注解是多例呢?因为有多个 AbstractConfig 对象呀~ - 详细的解析,见 「4.2 RelaxedDubboConfigBinder」 。
4.1.4 XXXDubboConfigConfiguration
// DubboAutoConfiguration.java |
- 关于
@EnableDubboConfig
注解的介绍,可以看看 《Dubbo 新编程模型之外部化配置 ——@EnableDubboConfig
》 。 SingleDubboConfigConfiguration 对应
@EnableDubboConfig(multiple = false)
。- 无任何条件,所以会创建。
引入了单个 Dubbo 配置绑定 Bean 的配置。即配置文件如下属性:
dubbo.application
dubbo.module
dubbo.registry
dubbo.protocol
dubbo.monitor
dubbo.provider
dubbo.consumer- ~
MultipleDubboConfigConfiguration 对应
@EnableDubboConfig(multiple = true)
。- 要求配置
"dubbo.config.multiple=true"
。默认情况下,Dubbo 自带"dubbo.config.multiple=true"
,所以也会创建。 引入了多个 Dubbo 配置绑定 Bean 的配置。即配置文件如下属性:
dubbo.applications
dubbo.modules
dubbo.registries
dubbo.protocols
dubbo.monitors
dubbo.providers
dubbo.consumers- ~
- 要求配置
4.2 RelaxedDubboConfigBinder
com.alibaba.boot.dubbo.autoconfigure.RelaxedDubboConfigBinder
,继承 AbstractDubboConfigBinder 抽象类,负责将 Spring Boot 的配置属性,注入到 Dubbo AbstractConfig 配置对象中。代码 如下:
// RelaxedDubboConfigBinder.java |
- AbstractDubboConfigBinder 属于 Dubbo 项目本身,所以本文就不解析逻。
<1.1>
处,调用父类的#getPropertySources()
方法,获得 PropertySource 数组。<1.2>
处,调用ConfigurationPropertySources#from(Iterable<PropertySource<?>> sources)
方法,转换成 ConfigurationPropertySource 数组。上述两个变量的值,如下图所示:`configurationPropertySources` 属性
<2>
处,调用Bindable#ofInstance(T instance)
方法,将dubboConfig
包装成 Bindable 对象。<3.1>
处,创建 Binder 对象。<3.2>
处,调用#getBindHandler()
方法,获得 BindHandler 对象。代码如下:// RelaxedDubboConfigBinder.java
private BindHandler getBindHandler() {
// 获得默认的 BindHandler 处理器
BindHandler handler = BindHandler.DEFAULT;
// 进一步包装成 IgnoreErrorsBindHandler 对象
if (isIgnoreInvalidFields()) {
handler = new IgnoreErrorsBindHandler(handler);
}
// 进一步包装成 NoUnboundElementsBindHandler 对象
if (!isIgnoreUnknownFields()) {
UnboundElementsSourceFilter filter = new UnboundElementsSourceFilter();
handler = new NoUnboundElementsBindHandler(handler, filter);
}
return handler;
}关于 BindHandler 类,胖友不用深究,只要知道如下即可。
有时候,绑定时可能需要实现额外的逻辑,而BindHandler接口提供了一个很好的方法来实现这一点。 每个BindHandler都可以实现onStart,onSuccess,onFailure和onFinish方法来覆盖行为。
Spring Boot提供了一些处理程序,主要用于支持现有的@ConfigurationProperties绑定。 例如,ValidationBindHandler可用于对绑定对象应用Validator验证。
<3.3>
处,调用Binder#bind(String name, Bindable<T> target, BindHandler handler)
方法,将配置中,指定前缀(prefix
)的属性,注入到 AbstractConfig 配置对象中。例如下图:`dubboConfig` 属性
如下开始,是
env
包下。
4.3 DubboDefaultPropertiesEnvironmentPostProcessor
com.alibaba.boot.dubbo.env.DubboDefaultPropertiesEnvironmentPostProcessor
,实现 EnvironmentPostProcessor、Ordered 接口,生成 Dubbo 默认的配置,添加到 environment
中。而需要生成的 Dubbo 默认的配置如下:
# 直接复用 spring.application.name |
- 因此,我们只要配置了
"spring.application.name"
的属性,"dubbo.application.name"
就会自动生成。
4.3.1 postProcessEnvironment
实现 #postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
方法,自动生成 Dubbo 默认配置。代码如下:
// DubboDefaultPropertiesEnvironmentPostProcessor.java |
<1>
处,调用#createDefaultProperties(ConfigurableEnvironment environment)
方法,生成 Dubbo 默认配置。代码如下:// DubboDefaultPropertiesEnvironmentPostProcessor.java
/**
* The property name of Spring Application
*
* @see ContextIdApplicationContextInitializer
*/
private static final String SPRING_APPLICATION_NAME_PROPERTY = "spring.application.name";
/**
* The property name of {@link ApplicationConfig}
*
* @see EnableDubboConfig
* @see EnableDubboConfigBinding
*/
private static final String DUBBO_APPLICATION_NAME_PROPERTY = "dubbo.application.name";
/**
* The property name of {@link EnableDubboConfig#multiple() @EnableDubboConfig.multiple()}
*/
private static final String DUBBO_CONFIG_MULTIPLE_PROPERTY = "dubbo.config.multiple";
/**
* The property name of {@link ApplicationConfig#getQosEnable() application's QOS enable}
*/
private static final String DUBBO_APPLICATION_QOS_ENABLE_PROPERTY = "dubbo.application.qos-enable";
private Map<String, Object> createDefaultProperties(ConfigurableEnvironment environment) {
Map<String, Object> defaultProperties = new HashMap<String, Object>();
// "dubbo.application.name"
setDubboApplicationNameProperty(environment, defaultProperties);
// "dubbo.config.multiple"
setDubboConfigMultipleProperty(defaultProperties);
// "dubbo.application.qos-enable"
setDubboApplicationQosEnableProperty(defaultProperties);
return defaultProperties;
}
private void setDubboApplicationNameProperty(Environment environment, Map<String, Object> defaultProperties) {
String springApplicationName = environment.getProperty(SPRING_APPLICATION_NAME_PROPERTY);
if (StringUtils.hasLength(springApplicationName)
&& !environment.containsProperty(DUBBO_APPLICATION_NAME_PROPERTY)) {
defaultProperties.put(DUBBO_APPLICATION_NAME_PROPERTY, springApplicationName);
}
}
private void setDubboConfigMultipleProperty(Map<String, Object> defaultProperties) {
defaultProperties.put(DUBBO_CONFIG_MULTIPLE_PROPERTY, Boolean.TRUE.toString());
}
private void setDubboApplicationQosEnableProperty(Map<String, Object> defaultProperties) {
defaultProperties.put(DUBBO_APPLICATION_QOS_ENABLE_PROPERTY, Boolean.FALSE.toString());
}- 虽然比较长,但是比较简单。
<2>
处,有 Dubbo 默认配置,则添加到environment
中。代码如下:// DubboDefaultPropertiesEnvironmentPostProcessor.java
private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
private void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map) {
// 情况一,获得到 "defaultProperties" 对应的 PropertySource 对象,则进行替换
MapPropertySource target = null;
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
if (source instanceof MapPropertySource) { // 找到
target = (MapPropertySource) source;
// 遍历 map 数组,进行替换到 "defaultProperties" 中
for (String key : map.keySet()) {
if (!target.containsProperty(key)) {
target.getSource().put(key, map.get(key));
}
}
}
}
// 情况二,不存在 "defaultProperties" 对应的 PropertySource 对象,则进行添加
if (target == null) {
target = new MapPropertySource(PROPERTY_SOURCE_NAME, map);
}
if (!propertySources.contains(PROPERTY_SOURCE_NAME)) {
propertySources.addLast(target);
}
}- 分成两种情况,也比较简单。胖友自己瞅瞅~
如下开始,是
context.event
包下。
4.4 WelcomeLogoApplicationListener
com.alibaba.boot.dubbo.context.event.WelcomeLogoApplicationListener
,实现 ApplicationListener 接口,处理 ApplicationEnvironmentPreparedEvent 事件,从而打印 Dubbo Banner 文本。代码如下:
// WelcomeLogoApplicationListener.java |
- 简单,就不多做解释了。
4.5 OverrideDubboConfigApplicationListener
com.alibaba.boot.dubbo.context.event.OverrideDubboConfigApplicationListener
,实现 ApplicationListener 接口,也是处理 ApplicationEnvironmentPreparedEvent 事件,根据 "dubbo.config.override"
的属性值,若为 true
时,则覆盖 environment
中 "dubbo."
开头的配置,添加到 Dubbo Properties 对象中。代码如下:
// OverrideDubboConfigApplicationListener.java |
<1>
处,获得"dubbo.config.override"
属性对应的值。默认情况下为true
。代码如下:// DubboUtils.java
/**
* The property name of override Dubbo config
* <p>
* The default value is {@link #DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE}
*/
public static final String OVERRIDE_CONFIG_PROPERTY_NAME = DUBBO_CONFIG_PREFIX + PROPERTY_NAME_SEPARATOR + "override";
/**
* The default property value of override Dubbo config
*/
public static final boolean DEFAULT_OVERRIDE_CONFIG_PROPERTY_VALUE = true;<2>
处,如果要重写,则覆盖添加到 Dubbo Properties 中。<2.1>
处,调用DubboUtils#filterDubboProperties(ConfigurableEnvironment environment)
方法,从environment
中,提取"dubbo."
开头的配置。代码如下:// DubboUtils.java
/**
* The separator of property name
*/
public static final String PROPERTY_NAME_SEPARATOR = ".";
/**
* The prefix of property name of Dubbo
*/
public static final String DUBBO_PREFIX = "dubbo";
/**
* Filters Dubbo Properties from {@link ConfigurableEnvironment}
*
* @param environment {@link ConfigurableEnvironment}
* @return Read-only SortedMap
*/
public static SortedMap<String, Object> filterDubboProperties(ConfigurableEnvironment environment) {
SortedMap<String, Object> dubboProperties = new TreeMap<>();
// 获得所有的配置
Map<String, Object> properties = EnvironmentUtils.extractProperties(environment);
// 遍历配置,如果以 "dubbo." 开头,则添加到 dubboProperties 中
for (Map.Entry<String, Object> entry : properties.entrySet()) {
String propertyName = entry.getKey();
if (propertyName.startsWith(DUBBO_PREFIX + PROPERTY_NAME_SEPARATOR)
&& entry.getValue() != null) {
dubboProperties.put(propertyName, entry.getValue().toString());
}
}
// 返回 dubboProperties
return Collections.unmodifiableSortedMap(dubboProperties);
}- 其中,
EnvironmentUtils#extractProperties(ConfigurableEnvironment environment)
方法,获得所有的配置。考虑到篇幅就不赘述,艿艿已经添加注释,点击 链接 查看。
- 其中,
<2.2>
处,调用 Dubbo 的ConfigUtils#getProperties()
方法,获得 Dubbo Properties 。然后再将dubboProperties
变量,添加到 Dubbo Properties 中。
4.6 AwaitingNonWebApplicationListener
com.alibaba.boot.dubbo.context.event.AwaitingNonWebApplicationListener
,实现 SmartApplicationListener 接口,实现在非 Web 的环境下,提供 JVM 不退出关闭的功能,即 JVM 一直运行着。
胖友可以试试,启动一个非 Web 环境的 Spring Boot 应用,然后会发现,JVM 会在启动完 Spring Boot 应用后,自动关闭。
4.6.1 supportsEventType
实现 #supportsEventType(Class<? extends ApplicationEvent> eventType)
方法,判断支持的事件类型是 ApplicationReadyEvent 和 ContextClosedEvent 。代码如下:
// AwaitingNonWebApplicationListener.java |
- 为什么呢,我们接着往下看。
4.6.2 supportsSourceType
实现 #supportsSourceType(Class<?> sourceType)
方法,判断支持的事件来源。代码如下:
// AwaitingNonWebApplicationListener.java |
- 全部返回
true
,意味支持所有的事件来源。
4.6.3 onApplicationEvent
// AwaitingNonWebApplicationListener.java |
<1>
处,当是 ApplicationReadyEvent 事件时,调用#onApplicationReadyEvent(ApplicationReadyEvent event)
方法,处理 ApplicationReadyEvent 事件。详细解析,见 「4.6.3.1 onApplicationReadyEvent」 。<2>
处,当是 ContextClosedEvent 事件时,调用#onApplicationReadyEvent(ContextClosedEvent event)
方法,处理 ApplicationReadyEvent 事件。详细解析,见 「4.6.3.2 onContextClosedEvent」 。
4.6.3.1 onApplicationReadyEvent
#onApplicationReadyEvent(ApplicationReadyEvent event)
方法,处理 ApplicationReadyEvent 事件。代码如下:
// AwaitingNonWebApplicationListener.java |
<1>
处,如果是 Web 环境,则直接返回。因为,已经提供了 JVM 不退出关闭的功能。<2>
处,调用#await()
方法,启动一个用户线程,从而实现等待。代码如下:// AwaitingNonWebApplicationListener.java
/**
* 是否已经等待完成
*/
private static final AtomicBoolean awaited = new AtomicBoolean(false);
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
protected void await() {
// has been waited, return immediately
// 如果已经处于阻塞等待,直接返回
if (awaited.get()) {
return;
}
// 创建任务,实现阻塞
executorService.execute(() -> executeMutually(() -> {
while (!awaited.get()) {
if (logger.isInfoEnabled()) {
logger.info(" [Dubbo] Current Spring Boot Application is await...");
}
try {
condition.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}));
}
private void executeMutually(Runnable runnable) {
try {
lock.lock();
// <X> 执行 Runnable
runnable.run();
} finally {
lock.unlock();
}
}- 基于 Lock + Condition 实现等待通知。
#executeMutually(Runnable runnable)
方法,被executorService
创建任务所调用。而该任务因为调用Condition#await()
方法,阻塞等待。那么此时,JVM 至少有一个该用户线程未运行结束,那么此时 JVM 关闭的条件 不被满足,所以就不会退出。
4.6.3.2 onApplicationReadyEvent
#onApplicationReadyEvent(ContextClosedEvent event)
方法,处理 ContextClosedEvent 事件。代码如下:
// AwaitingNonWebApplicationListener.java |
<1>
处,调用#release()
方法,进行释放。代码如下:// AwaitingNonWebApplicationListener.java
protected void release() {
executeMutually(() -> {
// CAS 设置 awaited 为 true
while (awaited.compareAndSet(false, true)) {
if (logger.isInfoEnabled()) {
logger.info(" [Dubbo] Current Spring Boot Application is about to shutdown...");
}
// 通知 Condition
condition.signalAll();
}
});
}- 通过调用
Condition#signalAll()
方法,通知 Condition 。从而在 「4.6.3.1 onApplicationReadyEvent」 中,启动的线程的阻塞,进行停止。
- 通过调用
<2>
处,调用#shutdown()
方法,关闭线程池。代码如下:// AwaitingNonWebApplicationListener.java
private void shutdown() {
if (!executorService.isShutdown()) {
// Shutdown executorService
executorService.shutdown();
}
}
5. dubbo-spring-boot-actuator
源码
dubbo-spring-boot-autoconfigure
模块,所有类如下图:`dubbo-spring-boot-autoconfigure` 模块
5.1 使用指南
使用时,需要导入 dubbo-spring-boot-actuator
依赖。即如下:
<!-- 导入 WEB 环境 --> |
观看本小节,胖友需要对 Spring Boot Actuator 有相关的了解。如果不知道,可以看看 《一起来学 SpringBoot 2.x | 第十四篇:强大的 Actuator 服务监控与管理》 文章。
另外,《Dubbo 官方文档 —— Dubbo Spring Boot Production-Ready》 文章,也是需要先瞅瞅的。
如果胖友使用的是 Spring Boot 2,有一个坑要注意,因为 《Spring Boot 2.0 的 Actuator 只暴露 health 和 info》 ,所以需要手动在配置文件中,添加要开启的 Dubbo Endpoint 。例如:
# application.properties |
- 此时,我们多开启了
dubbo
和dubboconfigs
这两个 EndPoint 。T T 坑了自己好久~
如下开始,是
autoconfigure
包下。
5.2 DubboEndpointsAutoConfiguration
com.alibaba.boot.dubbo.actuate.autoconfigure.DubboEndpointsAutoConfiguration
,Dubbo Endpoint 自动配置类。代码如下:
// DubboEndpointsAutoConfiguration.java |
- 每个方法,创建一个 Dubbo Endpint Bean 。一共有 6 个。
@PropertySource
注解,导入"classpath:/META-INF/dubbo-endpoins-default.properties"
配置文件。代码如下:# dubbo-endpoins-default.properties
# Dubbo Endpoints Default Properties is loaded by @PropertySource with low order,
# those values of properties can be override by higher PropertySource
# @see DubboEndpointsAutoConfiguration
# Set enabled for Dubbo Endpoints 设置 Dubbo Endpoints 是否开启
management.endpoint.dubbo.enabled = true
management.endpoint.dubboshutdown.enabled = false
management.endpoint.dubboconfigs.enabled = true
management.endpoint.dubboservices.enabled = false
management.endpoint.dubboreferences.enabled = false
management.endpoint.dubboproperties.enabled = true
# "management.endpoints.web.base-path" should not be configured in this file
# Re-defines path-mapping of Dubbo Web Endpoints 重命名 Dubbo Web Endpoints 路径
management.endpoints.web.path-mapping.dubboshutdown = dubbo/shutdown
management.endpoints.web.path-mapping.dubboconfigs = dubbo/configs
management.endpoints.web.path-mapping.dubboservices = dubbo/services
management.endpoints.web.path-mapping.dubboreferences = dubbo/references
management.endpoints.web.path-mapping.dubboproperties = dubbo/properties
5.3 DubboHealthIndicatorAutoConfiguration
com.alibaba.boot.dubbo.actuate.autoconfigure.DubboHealthIndicatorAutoConfiguration
,Dubbo Health Indicator 自动配置类。代码如下:
// DubboHealthIndicatorAutoConfiguration.java |
- 每个注解,看后面的代码注释。
- 唯一的方法,创建 DubboHealthIndicator Bean 对象。详细解析,见 「5.5 DubboHealthIndicator」 。
5.4 DubboHealthIndicatorProperties
com.alibaba.boot.dubbo.actuate.health.DubboHealthIndicatorProperties
,Dubbo Health Indicator Properties 类。代码代码如下:
// DubboHealthIndicatorProperties.java |
- 读取以
"management.health.dubbo"
开头的配置。
如下开始,是
health
包下。
5.5 DubboHealthIndicator
com.alibaba.boot.dubbo.actuate.health.DubboHealthIndicator
,继承 AbstractHealthIndicator 抽象类,Dubbo Health Indicator 实现类。代码如下:
5.4.1 doHealthCheck
实现 #doHealthCheck(Health.Builder builder)
方法,执行健康检查。代码如下:
在请求
/actuator/health
接口时,也会调用该方法。
// DubboHealthIndicator.java |
- 大体比较简单,胖友顺着注释来瞅瞅即可。
<2>
处,调用#resolveStatusCheckerNamesMap()
方法,解析 StatusChecker 的名字的 Map 。因为这个对后续逻辑非常关键,所以胖友先跳到 「5.4.2 resolveStatusCheckerNamesMap」 中。看完之后,在回到此处。- 最终返回
builder
的结果,如下图:`builder` 结果
5.4.2 resolveStatusCheckerNamesMap
#resolveStatusCheckerNamesMap()
方法,解析 StatusChecker 的名字的 Map。代码如下:
// DubboHealthIndicator.java |
<1>
处,调用#resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties()
方法,从 DubboHealthIndicatorProperties 中获取。代码如下:// DubboHealthIndicator.java
private DubboHealthIndicatorProperties dubboHealthIndicatorProperties;
private Map<String, String> resolveStatusCheckerNamesMapFromDubboHealthIndicatorProperties() {
// 获得 DubboHealthIndicatorProperties.Status
DubboHealthIndicatorProperties.Status status = dubboHealthIndicatorProperties.getStatus();
// 创建 Map
Map<String, String> statusCheckerNamesMap = new LinkedHashMap<>();
// 1. 读取 defaults 属性
for (String statusName : status.getDefaults()) {
statusCheckerNamesMap.put(statusName, PREFIX + ".status.defaults");
}
// 2. 读取 extras 属性
for (String statusName : status.getExtras()) {
statusCheckerNamesMap.put(statusName, PREFIX + ".status.extras");
}
return statusCheckerNamesMap;
}
<2>
处,调用#resolveStatusCheckerNamesMapFromProtocolConfigs()
方法,从 ProtocolConfig 中获取。代码如下:// DubboHealthIndicator.java
false) (required =
private Map<String, ProtocolConfig> protocolConfigs = Collections.emptyMap();
private Map<String, String> resolveStatusCheckerNamesMapFromProtocolConfigs() {
// 创建 Map
Map<String, String> statusCheckerNamesMap = new LinkedHashMap<>();
// 遍历 protocolConfigs
for (Map.Entry<String, ProtocolConfig> entry : protocolConfigs.entrySet()) {
// 获得 Bean 的名字
String beanName = entry.getKey();
// 获得 ProtocolConfig 对象
ProtocolConfig protocolConfig = entry.getValue();
// 获得 ProtocolConfig 的 StatusChecker 的名字的集合
Set<String> statusCheckerNames = getStatusCheckerNames(protocolConfig);
// 遍历 statusCheckerNames 数组
for (String statusCheckerName : statusCheckerNames) {
// 构建 source 属性
String source = buildSource(beanName, protocolConfig);
// 添加到 statusCheckerNamesMap 中
statusCheckerNamesMap.put(statusCheckerName, source);
}
}
return statusCheckerNamesMap;
}
private Set<String> getStatusCheckerNames(ProtocolConfig protocolConfig) {
String status = protocolConfig.getStatus();
return StringUtils.commaDelimitedListToSet(status);
}
private Set<String> getStatusCheckerNames(ProviderConfig providerConfig) {
String status = providerConfig.getStatus();
return StringUtils.commaDelimitedListToSet(status);
}
private String buildSource(String beanName, Object bean) {
return beanName + "@" + bean.getClass().getSimpleName() + ".getStatus()";
}
<3>
处,调用#resolveStatusCheckerNamesMapFromProviderConfig()
方法,从 ProviderConfig 中获取。代码如下:// DubboHealthIndicator.java
false) (required =
private Map<String, ProviderConfig> providerConfigs = Collections.emptyMap();
private Map<String, String> resolveStatusCheckerNamesMapFromProviderConfig() {
// 创建 Map
Map<String, String> statusCheckerNamesMap = new LinkedHashMap<>();
// 遍历 providerConfigs
for (Map.Entry<String, ProviderConfig> entry : providerConfigs.entrySet()) {
// 获得 Bean 的名字
String beanName = entry.getKey();
// 获得 ProviderConfig 对象
ProviderConfig providerConfig = entry.getValue();
// 获得 ProtocolConfig 的 StatusChecker 的名字的集合
Set<String> statusCheckerNames = getStatusCheckerNames(providerConfig);
// 遍历 statusCheckerNames 数组
for (String statusCheckerName : statusCheckerNames) {
// 构建 source 属性
String source = buildSource(beanName, providerConfig);
// 添加到 statusCheckerNamesMap 中
statusCheckerNamesMap.put(statusCheckerName, source);
}
}
return statusCheckerNamesMap;
}
如下开始,是
endpoint
包下。
5.6 AbstractDubboEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.AbstractDubboEndpoint
,实现 ApplicationContextAware、EnvironmentAware 接口,Dubbo Endpoint 抽象类,提供给子类工具方法。
5.6.1 基本属性
// AbstractDubboEndpoint.java |
5.6.2 resolveBeanMetadata
#resolveBeanMetadata(Object bean)
方法,获得 Bean 的元数据。代码如下:
// AbstractDubboEndpoint.java |
5.6.3 getServiceBeansMap
#getServiceBeansMap()
方法,获得所有 ServiceBean 。代码如下:
// AbstractDubboEndpoint.java |
5.6.4 getProtocolConfigsBeanMap
#getProtocolConfigsBeanMap()
方法,获得所有 ProtocolConfig 。代码如下:
// AbstractDubboEndpoint.java |
5.6.5 getReferenceAnnotationBeanPostProcessor
#getReferenceAnnotationBeanPostProcessor()
方法,获得 ReferenceAnnotationBeanPostProcessor Bean 对象。代码如下:
// AbstractDubboEndpoint.java |
5.7 DubboEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboEndpoint
,Dubbo Endpoint ,获得 Dubbo Meta Data(元数据)。代码如下:
// DubboEndpoint.java |
5.8 DubboConfigsMetadataEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboConfigsMetadataEndpoint
,继承 AbstractDubboEndpoint 抽象类,获得 所有的 Dubbo 配置类的元数据。代码如下:
// DubboConfigsMetadataEndpoint.java |
5.7 DubboPropertiesEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboPropertiesEndpoint
,继承 AbstractDubboEndpoint 抽象类,获得 Dubbo Properties 。代码如下:
// DubboPropertiesEndpoint.java |
5.8 DubboReferencesMetadataEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboReferencesMetadataEndpoint
,继承 AbstractDubboEndpoint 抽象类,获得所有的 Dubbo @Reference
Bean 的元数据。代码如下:
// DubboReferencesMetadataEndpoint.java |
5.9 DubboServicesMetadataEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboServicesMetadataEndpoint
,继承 AbstractDubboEndpoint 抽象类,获得所有的 Dubbo @Service
Bean 的元数据。代码如下:
// DubboServicesMetadataEndpoint.java |
5.10 DubboShutdownEndpoint
com.alibaba.boot.dubbo.actuate.endpoint.DubboShutdownEndpoint
,继承 AbstractDubboEndpoint 抽象类,关闭 Dubbo 。代码如下:
// DubboShutdownEndpoint.java |
- 通过调用该接口,我们就可以远程关闭 Dubbo 服务。
666. 彩蛋
总的来说,比较简单。实际上,比较重的逻辑,在 Dubbo 本身上。所以呢,感兴趣的胖友,可以自己去撸一撸。嘻嘻嘻