Dubbo启动流程
伴随着SpringBoot容器的启动,Dubbo 主要是做了如下几件事情:
- 将解析属性配置的class 和解析注解(
@Service、@Reference等等)配置的class 注册到beanDefinitionMap- 将解析后的配置类(
AnnotationAttributes)和Dubbo的@Service服务,注册到beanDefinitionMap- 当Spring创建Bean、填充属性时,对@Reference 注解标注的属性做注入(inject)
- 通过监听
ContextRefreshedEvent事件,调用DubboBootstrap 启动器,暴露@Service服务到注册中心
Dubbo启动流程图
下面是源码逻辑
属性配置解析器和注解解析器
@Dubbo、@EnableDubboConfig、@DubboComponentScan
当你使用
@EnableDubbo注解启动Dubbo的时候,会加载它的@EnableDubboConfig和@DubboComponentScan注解,分别用于处理Dubbo属性配置和解析Dubbo服务
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
// 处理配置文件中(例如yaml文件)的dubbo配置
@EnableDubboConfig
// 扫描并解析使用了dubbo注解的组件(例如@Service)
@DubboComponentScan
public @interface EnableDubbo {
......
}
@EnableDubboConfig和@DubboComponentScan注解使用了Spring的@Import注解来加载具体的实现类。
@Import(DubboConfigConfigurationRegistrar.class)
@Import(DubboComponentScanRegistrar.class)
DubboConfigConfigurationRegistrar
DubboConfigConfigurationRegistrar用于将不同的属性加载到不同的配置文件中
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
boolean multiple = attributes.getBoolean("multiple");
// 加载单个的属性,例如<dubbo.protocol>
registerBeans(registry, DubboConfigConfiguration.Single.class);
if (multiple) {
//加载组合属性,例如<dubbo.protocols>
registerBeans(registry, DubboConfigConfiguration.Multiple.class);
}
// 将通用class注册到beanDefinitionMap
// 例如@Reference 注解的后置处理器:ReferenceAnnotationBeanPostProcessor.class
registerCommonBeans(registry);
}
}
registerBeans方法最终通过ConfigurationBeanBindingsRegister将解析之后的配置类注册到BeanDefinitionMap
public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private ConfigurableEnvironment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//省略部分代码.....
for (AnnotationAttributes element : annotationAttributes) {
//将解析后的对象注册到BeanDefinitionMap
registrar.registerConfigurationBeanDefinitions(element, registry);
}
}
}
最终在Spring 容器中他们会被初始化成若干对象,例如:
dubbo:registry会转换成org.apache.dubbo.config.RegistryConfig#0
DubboComponentScanRegistrar
DubboComponentScanRegistrar主要是用来将ServiceAnnotationBeanPostProcessor注册到BeanDefinitionMap中。
在Spring调用BeanFactory相关的后置处理器(invokeBeanFactoryPostProcessors)时,会使用ServiceAnnotationBeanPostProcessor将@DubboService相关注解注册到BeanDefinitionMap
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//省略部分代码......
//将ServiceAnnotationBeanPostProcessor 注册到Spring容器
registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
// 将通用class注册到beanDefinitionMap
// 例如@Reference 注解的后置处理器:
registerCommonBeans(registry);
}
}
//注册ServiceAnnotationBeanPostProcessor 的方法
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
在
ServiceAnnotationBeanPostProcessor中,真正做注解解析注册的是他的父类ServiceClassPostProcessor
在
ServiceClassPostProcessor中,它注册了一个dubbo监听器,用于监听Spring容器的刷新、关闭事件,同时也将@DubboService注解的类注册到了BeanDefinitionMap中.
public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
//省略部分代码...
//注册Dubbo监听器,建东Spring容器的事件
registerBeans(registry, DubboBootstrapApplicationListener.class);
//注册Dubbo 组件Bean
registerServiceBeans(resolvedPackagesToScan, registry);
}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
//省略部分代码...
for (String packageToScan : packagesToScan) {
scanner.scan(packageToScan);
//将@ComponentScan 组件扫描到的,并且是@DubboService 标注的Bean导出
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
//注册Dubbo 的Service Bean
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
}
}
}
至此,被
@DubboService(2.7.7)、@Service(2.7.0\alibaba) 注解标注的Bean 就被注册到了IOC容器,成为IOC 中Bean 集合的一员。
DubboBootstrap 启动器
伴随着Spring 容器的启动,在
invokeBeanFactoryPostProcessors阶段我们注册了dubbo相关的组件到IOC,在finishBeanFactoryInitialization(beanFactory)Dubbo的组件被初始化、实例化,最后Dubbo通过监听Spring事件的方式完成启动器的调用、服务导出等操作
DubboBootstrap
DubboBootstrap的启动是通过监听Spring事件实现的。Spring会在容器Refresh 的最后一步发送一个事件ContextRefreshedEvent,表示容器刷新完毕。
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
}
对于
ContextRefreshedEvent事件的监听,最终调用了dubboBootstrap.start() 方法,在这个方法里,Dubbo 完成了对服务的导出(暴露),导出服务的过程中,用到了DUBBO SPI机制
public class DubboBootstrap extends GenericEventListener {
public DubboBootstrap start() {
//省略部分代码...
//导出Dubbo 服务
exportServices();
//处理ReferenceConfig对象,添加异步任务
referServices();
}
}
//这里最终调用了ServiceConfig 的export()
private void exportServices() {
configManager.getServices().forEach(sc -> {
// TODO, compatible with ServiceConfig.export()
ServiceConfig serviceConfig = (ServiceConfig) sc;
serviceConfig.setBootstrap(this);
if (exportAsync) {
ExecutorService executor = executorRepository.getServiceExporterExecutor();
Future<?> future = executor.submit(() -> {
sc.export();
exportedServices.add(sc);
});
asyncExportingFutures.add(future);
} else {
sc.export();
exportedServices.add(sc);
}
});
}
ServiceConfig
ServiceConfig 最终发送了一个事件:ServiceConfigExportedEvent,通过事件通知的方式实现导出逻辑。
public void exported() {
// dispatch a ServiceConfigExportedEvent since 2.7.4
dispatch(new ServiceConfigExportedEvent(this));
}
ServiceNameMappingListener
public class ServiceNameMappingListener implements EventListener<ServiceConfigExportedEvent> {
private final ServiceNameMapping serviceNameMapping = getDefaultExtension();
//这里监听并解析了DubboReference 用到的主要参数
@Override
public void onEvent(ServiceConfigExportedEvent event) {
ServiceConfig serviceConfig = event.getServiceConfig();
List<URL> exportedURLs = serviceConfig.getExportedUrls();
exportedURLs.forEach(url -> {
String serviceInterface = url.getServiceInterface();
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
String protocol = url.getProtocol();
serviceNameMapping.map(serviceInterface, group, version, protocol);
});
}
}
ServiceNameMapping 及其子类承接了对注册中心的调用,以nacos 为例调用逻辑如下图:
文字描述一下这个过程就是:
DynamicConfigurationServiceNameMapping.map
-> CompositeDynamicConfiguration.publishConfig
-> NacosDynamicConfiguration.publishConfig
public class DynamicConfigurationServiceNameMapping implements ServiceNameMapping {
@Override
public void map(String serviceInterface, String group, String version, String protocol) {
if (IGNORED_SERVICE_INTERFACES.contains(serviceInterface)) {
return;
}
//在这里存储了所有的注册中心组件
//返回一个CompositeDynamicConfiguration 实例
DynamicConfiguration dynamicConfiguration = DynamicConfiguration.getDynamicConfiguration();
// the Dubbo Service Key as group
// the service(application) name as key
// It does matter whatever the content is, we just need a record
String key = getName();
String content = valueOf(System.currentTimeMillis());
execute(() -> {
//调用CompositeDynamicConfiguration,然后由他来调用具体的注册中心
dynamicConfiguration.publishConfig(key, buildGroup(serviceInterface, group, version, protocol), content);
if (logger.isInfoEnabled()) {
logger.info(String.format("Dubbo service[%s] mapped to interface name[%s].",
group, serviceInterface, group));
}
});
}
nacos 的注册中心代码实现如下:
public class NacosDynamicConfiguration implements DynamicConfiguration {
@Override
public boolean publishConfig(String key, String group, String content) {
boolean published = false;
String resolvedGroup = resolveGroup(group);
try {
//这里的configService 就是nacos 对应的api了
String value = configService.getConfig(key, resolvedGroup, getDefaultTimeout());
if (StringUtils.isNotEmpty(value)) {
content = value + "," + content;
}
published = configService.publishConfig(key, resolvedGroup, content);
} catch (NacosException e) {
logger.error(e.getErrMsg());
}
return published;
}
}
至此dubbo 的服务就顺利的暴露到注册中心了,dubbo 也基本完成了他的启动工作。