前言
源码地址:dubbo
版本: dubbo-parent pom中的version为
2.7.12-SNAPSHOT
测试代码:dubbo-demo module。在application.properties中,加入了registry url如下
dubbo.registry.address=zookeeper://127.0.0.1:2181
在dubbo中一切都是基于interface而来,对provider,consumer都是。在代码层面上对应AbstractInterfaceConfig,而接口又是以方法粒度扩展对应AbstractMethodConfig。
从代码层次上来看
AbstractServiceConfig:与服务提供方有关
- ServiceConfig -> 对应的spring bean为子类ServiceBean
- ProviderConfig
AbstractReferenceConfig:与服务消费方有关
- ReferenceConfig->对应的spring bean为子类ReferenceBean
- ConsumerConfig
启动入口
DubboBootstrapApplicationListener :继承自 OnceApplicationContextEventListener,该监听器只触发一次容器事件监听的执行避免事件传播,适合用于容器初始化完成和关闭等,定义的order为最低优先级保证当容器完全初始完成后最后执行。
onContextRefreshedEvent((ContextRefreshedEvent) event);
onContextClosedEvent((ContextClosedEvent) event);
当spring容器初始化完成后,该监听器会监听到ContextRefreshedEvent事件并执行DubboBootstrap的start,其中包括初始化、暴露服务、引用服务等所有流程,本次主要分析服务暴露的部分。
initialize:负责与dubbo相关的服务初始化,例如startConfigCenter会使用之前存放了相关配置信息(ApplicationConfig,ConfigCenterConfig等)的ConfigManager,将合适configCenter作为DynamicConfiguration存到environment
服务发布
DubboBootstrapApplicationListener监听到容器启动事件ContextRefreshedEvent
调用DubboBootstrap的start,其中与服务发布有关的为以下几个流程
exportServices
负责发布扫描到的dubbo service提供者。
DubboBootstrap中由configManager负责收集跟dubbo相关的服务,注册,协议等信息,并在此时直接定位要发布服务ServiceConfig;
根据需要异步或同步发布服务(ExecutorRepository也是dubbo扩展加载类的一种,利用executor异步处理发布每个service的任务);
exportService(ServiceConfig sc):执行ServiceConfig的export,并缓存到Map<String, ServiceConfigBase<?>> exportedServices中
ServiceConfig export
发布检验
比如是否设置了export为false,需要delay等
doExport
主要功能为doExportUrls。这里采用zookeeper注册中心为例
// 构造registry相关信息
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
拿到的registry url例如:registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider&dubbo=2.0.2&id=org.apache.dubbo.config.RegistryConfig#0&pid=13300®istry=zookeeper×tamp=1627216227129;
依次对配置的protocols解析得到的ProtocolConfig,得到该服务对应在注册中心上(这里为zk)的path,一般为接口全路径名例如org.apache.dubbo.demo.DemoService, 并添加到ServiceRepository缓存中;调用doExportUrlsFor1Protocol处理对应协议的发布。
doExportUrlsFor1Protocol
- 构造url所需要的参数信息:比如side(provider),application,interface,methods及运行时信息等
- 对GenericService的情况进行处理:非generic时,会通过Wrapper找到该接口下对应要暴露的方法等,生成第一个interface->Wrapper0类存在于Wrapper的缓存中
if (ProtocolUtils.isGeneric(generic)) {
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
} else {
....
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
- 解析host和port:用于定位ip和端口被消费方调用
- 构造服务方URL:用于服务方在注册中心的地址,例如,dubbo://172.xx.xx.x:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bind.ip=172.xx.xx.x&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=13300&release=&service.name=ServiceBean:/org.apache.dubbo.demo.DemoService&side=provider×tamp=1627219556526
- ConfiguratorFactory:提供SPI接口扩展处理url参数信息的api,Configurator getConfigurator(URL url)
- scope决定发布类型:由目前url中的scope参数判断如何发布,none则不发布,不是remote(null的情况也匹配)则exportLocal(这里将执行),再接着不是local(null的情况也匹配)则进行远程发布(这里也会执行);
对第六步中不同方式发布进行下一步分析
exportLocal
用local URL的方式通过Protocol$Adaptive发布
//ServiceConfig中默认初始化了protocol和proxyFactory自适应代理类
private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
//exportLocal中,这里实际执行StubProxyFactoryWrapper和InjvmProtocol的接口方法
Exporter<?> exporter = PROTOCOL.export(PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
其中StubProxyFactoryWrapper是个包装类,里面包裹的真正实现是JavassistProxyFactory(因为ProxyFactory的扩展目录中有符合条件的包装类StubProxyFactoryWrapper对其进行自动封装),因此会真正执行Javasist的export&#