前言
前面那篇文章简单介绍了Dubbo是什么,而今天的这篇文章将正式展开对其源码的解析,为我们进入更深层次的探索铺平了道路。
环境配置
使用的Dubbo版本为3.2.0,注册中心使用zookeeper,demo为官方springboot演示代码。
Provide启动
我们看到springboot启动类上多了一个@EnableDubbo注解,我们点进这个注解发现它其实@EnableDubboConfig、@DubboComponentScan两个注解。
java
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
java
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
java
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
我们点进DubboConfigConfigurationRegistrar这个类
java
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// initialize dubbo beans
DubboSpringInitializer.initialize(registry);
}
}
java
public class DubboSpringInitializer {
public static void initialize(BeanDefinitionRegistry registry) {
//.......省略非核心代码
// init dubbo context
initContext(context, registry, beanFactory);
}
private static void initContext(DubboSpringInitContext context, BeanDefinitionRegistry registry,
ConfigurableListableBeanFactory beanFactory) {
// register common beans
DubboBeanUtils.registerCommonBeans(registry);
}
static void registerCommonBeans(BeanDefinitionRegistry registry) {
......
//这里主要是处理@DubboReference和@Reference注解
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// register ApplicationListeners 注册监听器,这里很重要
registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
// Dubbo config initializer 加载配置
registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
......
}
这里我们可以看到@EnableDubboConfig注解的作用是加载dubbo的配置,注册两个监听器(很重要,先记下)DubboDeployApplicationListener、DubboConfigApplicationListener,进行服务提供方的初始化工作。我们接着看下一个注解
java
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// initialize dubbo beans
DubboSpringInitializer.initialize(registry);
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerServiceAnnotationPostProcessor(packagesToScan, registry);
}
private void registerServiceAnnotationPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
}
这里我们可以看到创建了一个ServiceAnnotationPostProcessor的BeanDefinition,它是spring中的后置处理器,这里为了直观,我们直接开启debug断点 可以看到服务提供者的实现类被扫描到后组装成了BeanDefinition,其实@DubboComponentScan注解的核心职责就是将服务提供者的实现类扫描后交给spring的容器进行管理,这里就牵扯到spring的启动流程及bean的生命周期相关知识了,还记得前面注册的监听器吗,这里我们直接找到这个DubboConfigApplicationListener监听器代码 可以看到,在spring刷新过程中的registerListeners()方法中,前面注册的监听器会接受到事件进行处理
java
@Override
public void prepare() {
applicationDeployer.initialize();
this.initialize();
}
@Override
public void initialize() {
if (initialized) {
return;
}
// Ensure that the initialization is completed when concurrent calls
synchronized (startLock) {
if (initialized) {
return;
}
onInitialize();
// register shutdown hook
registerShutdownHook();
startConfigCenter();
loadApplicationConfigs();
initModuleDeployers();
initMetricsReporter();
initMetricsService();
// @since 2.7.8
startMetadataCenter();
initialized = true;
}
}
@Override
public void initialize() throws IllegalStateException {
if (initialized) {
return;
}
// Ensure that the initialization is completed when concurrent calls
synchronized (this) {
if (initialized) {
return;
}
onInitialize();
loadConfigs();
// read ModuleConfig
ModuleConfig moduleConfig = moduleModel.getConfigManager().getModule().orElseThrow(() -> new IllegalStateException("Default module config is not initialized"));
exportAsync = Boolean.TRUE.equals(moduleConfig.getExportAsync());
referAsync = Boolean.TRUE.equals(moduleConfig.getReferAsync());
// start in background
background = moduleConfig.getBackground();
if (background == null) {
// compatible with old usages
background = isExportBackground() || isReferBackground();
}
initialized = true;
}
}
这两步initialize所做的时间方法比较简单,看方法就能知道各个子方法的职责,就不过多赘述了。 前面说到有两个很重要的监听器,我们还有DubboDeployApplicationListener监听器没讲到,我们进入这个监听器。
服务暴露
java
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (nullSafeEquals(applicationContext, event.getSource())) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
ModuleDeployer deployer = moduleModel.getDeployer();
Assert.notNull(deployer, "Module deployer is null");
// start module 服务暴露
Future future = deployer.start();
......
}
java
@Override
public Future start() throws IllegalStateException {
// initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
applicationDeployer.initialize();
return startSync();
}
private synchronized Future startSync() throws IllegalStateException {
......
// export services 服务暴露
exportServices();
private void exportServices() {
for (ServiceConfigBase sc : configManager.getServices()) {
exportServiceInternal(sc);
}
}
private void exportServiceInternal(ServiceConfigBase sc) {
......
} else {
if (!sc.isExported()) {
//服务暴露
sc.export();
exportedServices.add(sc);
}
}
}
private void doExportUrls() {
......
//获取注册的URL
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
MetricsEventBus.post(RegistryEvent.toRsEvent(module.getApplicationModel(), getUniqueServiceName(), protocols.size() * registryURLs.size()),
() -> {
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// stub service will use generated service name
if (!serverService) {
// In case user specified path, register service one more time to map it to path.
repository.registerService(pathKey, interfaceClass);
}
//进行服务暴露
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
return null;
}
);
providerModel.setServiceUrls(urls);
这里有本地暴露和远程暴露,我们直接看远程暴露的代码 中间的wapper调用链比较长,我们知道dubbo底层通讯框架使用的netty,我们直接断点到NettyServer的代码处 最后的最后的,就是暴露给我们的注册中心zk了。
总结
最后用一段话总结一下,服务暴露的流程始于Spring IOC容器完成刷新后,获取需要导出的接口信息,组装成本地暴露和远程暴露的URL,通过javassist 动态封装引用(实现类),将其统一暴露为Invoker,隐藏底层实现细节,封装成exporter,以方便调用方使用,接着启动Netty服务器,最后将URL注册的配置中心。
这篇dubbo服务暴露的源码解析文章,深入挖掘了服务启动暴露的过程,虽然非核心环节已经略过,但因为涉及的内容丰富,导致了整篇文章的篇幅较长。对dubbo源码感兴趣的朋友,我强烈建议您自己对核心流程进行debug一遍,深入了解dubbo服务的内部运作机制。