1. 环境搭建
-
代码已经上传至 https://github.com/masteryourself/dubbo ,分支名称是
masteryourself-2.7.3-release
-
provider 是
dubbo-demo-xml-provider
工程,启动类是Application
-
consumer 是
dubbo-demo-xml-consumer
工程,启动类是Application
2. 源码解析
2.1 关于 ServiceBean
- 由于
ServiceBean
实现了ApplicationListener<ContextRefreshedEvent>
接口,所以会在onApplicationEvent
方法中监听ContextRefreshedEvent
事件,当容器刷新完毕后,会调用export()
方法完成服务暴露
2.2 流程预览
// 1. 监听 spring 的 【ContextRefreshedEvent】 事件
org.apache.dubbo.config.spring.ServiceBean#onApplicationEvent ->
// 调用 export() 方法完成服务暴露
org.apache.dubbo.config.spring.ServiceBean#export ->
// 调用父类 【ServiceConfig】 完成服务暴露
org.apache.dubbo.config.ServiceConfig#export ->
// 1.1 检查和更新属性
org.apache.dubbo.config.ServiceConfig#checkAndUpdateSubConfigs ->
// 设置全局的默认属性,因为 serviceBean 和 application、registries 等配置类都有关联关系
org.apache.dubbo.config.ServiceConfig#completeCompoundConfigs
// 启动全局配置中心
org.apache.dubbo.config.AbstractInterfaceConfig#startConfigCenter ->
// 刷新 ApplicationConfig、MonitorConfig、ModuleConfig、ProtocolConfig、RegistryConfig、ProviderConfig、ConsumerConfig 配置
org.apache.dubbo.config.context.ConfigManager#refreshAll
// 1.1.1(*) 获取混合配置,给 set 开头的方法赋值,即完成属性赋值
// 配置优先级默认是:系统环境变量 -> 配置中心某个应用的配置 -> 配置中心的全局配置 -> AbstractConfig 类的属性值 ->
// dubbo.properties.file 或 dubbo.properties 文件配置
org.apache.dubbo.config.AbstractConfig#refresh
// 1.1.1.1(*) 初始化 compositeConfiguration 混合配置 list
org.apache.dubbo.common.config.Environment#getConfiguration
// 1.2 导出服务
org.apache.dubbo.config.ServiceConfig#doExport
// 1.2.1(*) 循环所有的 protocols,依次进行服务暴露,这里的 protocols 有 2 个,因此一共会暴露 4 个地址
// 0 = {ProtocolConfig@2823} "<dubbo:protocol name="dubbo" port="20880" valid="true" id="dubbo" prefix="dubbo.protocols." />"
// 1 = {ProtocolConfig@2840} "<dubbo:protocol name="dubbo" port="20881" valid="true" id="dubbo2" prefix="dubbo.protocols." />"
org.apache.dubbo.config.ServiceConfig#doExportUrls
// 1.2.1.1 获取要导出的服务 url,格式如下:
// 0 = {URL@2835} "registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&
// dubbo=2.0.2&pid=7208&qos-port=22222®istry=zookeeper×tamp=1577007384243"
// 1 = {URL@2836} "registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&
// dubbo=2.0.2&pid=7208&qos-port=22222®istry=zookeeper×tamp=1577007384245&version=1.1.0"
org.apache.dubbo.config.AbstractInterfaceConfig#loadRegistries
// 1.2.1.2(*) 调用此方法进行服务暴露
org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
// 1.2.1.2.1(*) 改写 url,把协议改成 injvm 协议,host 设置为 127.0.0.1,端口号设置为 0
// 如果配置了 remote,则表示禁止使用 injvm 协议,就不会进行本地暴露
org.apache.dubbo.config.ServiceConfig#exportLocal ->
// 调用 Protocol 的动态代理类 【Protocol$Adaptive】 的 export 方法,先经过 wrapper 包装类,再到 【InjvmProtocol】,因为协议被改成了 injvm
org.apache.dubbo.rpc.Protocol$Adaptive#export ->
// ProtocolListener 包装类,当 protocol 不为 registry 时才会起作用,这里是 injvm,所以会起作用
// 服务导出之后,可以用监听器做一些操作
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#export ->
// ProtocolFilter 包装类,当 protocol 不为 registry 时才会起作用,这里是 injvm,所以会起作用
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#export
// 1.2.1.2.1.1(*) 构建 filterChain,实现类最终会被包装成
// EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter -> TraceFilter -> TimeoutFilter ->
// MonitorFilter -> ExceptionFilter -> DemoServiceImpl
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain
// InjvmProtocol,真正处理 injvm 协议的 protocol 类
org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol#export ->
// 在构造方法里把 invoker 添加到了 【exporterMap】 属性中,在 injvm 协议调用时会从此 map 取值
// "org.apache.dubbo.demo.DemoService" -> "org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper$CallbackRegistrationInvoker@5827af16"
org.apache.dubbo.rpc.protocol.injvm.InjvmExporter#<init>
// 1.2.1.2.2 循环所有的 registryURLs,作远程服务导出,形如:
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&
// export=dubbo%3A%2F%2F192.168.89.1%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3D
// demo-provider%26bean.name%3Dorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.89.1%26bind.port%3D20880%26
// deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26
// methods%3DsayHello%26pid%3D18816%26qos-port%3D22222%26register%3Dtrue%26release%3D%26side%3Dprovider%26
// timestamp%3D1577009068166&pid=18816&qos-port=22222®istry=zookeeper×tamp=1577009067710
// 调用 Protocol 的动态代理类 【Protocol$Adaptive】 的 export 方法,先经过 wrapper 包装类,再到 【RegistryProtocol】,因为协议是 registry
org.apache.dubbo.rpc.Protocol$Adaptive#export ->
// ProtocolListener 包装类,当 protocol 不为 registry 时才会起作用,这里是 registry,所以不会起作用
// 服务导出之后,可以用监听器做一些操作
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#export ->
// ProtocolFilter 包装类,当 protocol 不为 registry 时才会起作用,这里是 registry,所以不会起作用
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#export
// 1.2.1.2.2.1(*) 添加监听器,暴露远程服务,连接 zk 创建节点
org.apache.dubbo.registry.integration.RegistryProtocol#export
// 1.2.1.2.2.1.1 暴露远程服务,形如:
// dubbo://192.168.89.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&
// bean.name=org.apache.dubbo.demo.DemoService&bind.ip=192.168.89.1&bind.port=20880&deprecated=false&
// dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&
// pid=8724&qos-port=22222®ister=true&release=&side=provider×tamp=1577011539455
org.apache.dubbo.registry.integration.RegistryProtocol#doLocalExport ->
// 调用 Protocol 的动态代理类 【Protocol$Adaptive】 的 export 方法,先经过 wrapper 包装类,再到 【DubboProtocol】
org.apache.dubbo.rpc.Protocol$Adaptive#export ->
// ProtocolListener 包装类,当 protocol 不为 registry 时才会起作用,这里是 dubbo,所以会起作用
// 服务导出之后,可以用监听器做一些操作
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#export ->
// ProtocolFilter 包装类,当 protocol 不为 registry 时才会起作用,这里是 dubbo,所以会起作用同 【1.2.1.2.1.1】
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#export ->
// 获取 url,暴露 netty 远程服务
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#export ->
// 获取 netty server,绑定服务名称和端口号
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#openServer
// 1.2.1.2.2.1.2 把服务注册到 zk 上
org.apache.dubbo.registry.integration.RegistryProtocol#register ->
// 调用 zookeeper 的 doRegister 方法,把服务路径写到 zk 节点上
org.apache.dubbo.registry.support.FailbackRegistry#doRegister
2.3 流程详解
2.3.1 AbstractConfig#refresh(1.1.1)
org.apache.dubbo.config.AbstractConfig
public void refresh() {
try {
// 获取混合配置
// 1. 系统环境变量
// 2. 配置中心某个应用的配置
// 3. 配置中心的全局配置
// 4. dubbo.properties.file 或 dubbo.properties 文件配置
CompositeConfiguration compositeConfiguration = Environment.getInstance().getConfiguration(getPrefix(), getId());
// 获取 AbstractConfig 类中属性的值,即调用 getXxx 或 isXxx 方法返回的所有 metaData 值
InmemoryConfiguration config = new InmemoryConfiguration(getPrefix(), getId());
config.addProperties(getMetaData());
// 判断是否是配置中心的配置优先
// 配置优先级默认是:系统环境变量 -> 配置中心某个应用的配置 -> 配置中心的全局配置 -> AbstractConfig 类的属性值 ->
// dubbo.properties.file 或 dubbo.properties 文件配置
if (Environment.getInstance().isConfigCenterFirst()) {
// The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration
compositeConfiguration.addConfiguration(3, config);
} else {
// The sequence would be: SystemConfiguration -> AbstractConfig -> AppExternalConfiguration -> ExternalConfiguration -> PropertiesConfiguration
compositeConfiguration.addConfiguration(1, config);
}
// loop methods, get override value and set the new value back to method
Method[] methods = getClass().getMethods();
for (Method method : methods) {
// 获取 set 开头的方法
if (MethodUtils.isSetter(method)) {
try {
// 根据 setXxx 的 xxx 属性,从 compositeConfiguration 混合配置中获取属性对应的值
String value = StringUtils.trim(compositeConfiguration.getString(extractPropertyName(getClass(), method)));
// isTypeMatch() is called to avoid duplicate and incorrect update, for example, we have two 'setGeneric' methods in ReferenceConfig.
if (StringUtils.isNotEmpty(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value)) {
// 反射调用 set 方法赋值
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (NoSuchMethodException e) {
logger.info("Failed to override the property " + method.getName() + " in " +
this.getClass().getSimpleName() +
", please make sure every property has getter/setter method provided.");
}
}
}
} catch (Exception e) {
logger.error("Failed to override ", e);
}
}
2.3.2 Environment#getConfiguration(1.1.1.1)
org.apache.dubbo.common.config.Environment
public CompositeConfiguration getConfiguration(String prefix, String id) {
CompositeConfiguration compositeConfiguration = new CompositeConfiguration();
// Config center has the highest priority
// 从系统环境变量中获取值
compositeConfiguration.addConfiguration(this.getSystemConfig(prefix, id));
// 从配置中心的 dubbo.config.xxx.dubbo.properties 节点获取值(xxx 应用的配置)
compositeConfiguration.addConfiguration(this.getAppExternalConfig(prefix, id));
// 从配置中心的 dubbo.config.dubbo.dubbo.properties 节点获取值(全局配置)
compositeConfiguration.addConfiguration(this.getExternalConfig(prefix, id));
// 从 dubbo.properties.file 或者 dubbo.properties 文件中获取值
compositeConfiguration.addConfiguration(this.getPropertiesConfig(prefix, id));
return compositeConfiguration;
}
2.3.3 ServiceConfig#doExportUrls(1.2.1)
org.apache.dubbo.config.ServiceConfig
private void doExportUrls() {
// 获取要导出的服务 url,格式如下
// 0 = {URL@2835} "registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&
// dubbo=2.0.2&pid=7208&qos-port=22222®istry=zookeeper×tamp=1577007384243"
// 1 = {URL@2836} "registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&
// dubbo=2.0.2&pid=7208&qos-port=22222®istry=zookeeper×tamp=1577007384245&version=1.1.0"
List<URL> registryURLs = loadRegistries(true);
// 循环所有的 protocols,挨个导出服务,protocols 有如下两个,所以一共会暴露 4 个服务
// 0 = {ProtocolConfig@2823} "<dubbo:protocol name="dubbo" port="20880" valid="true" id="dubbo" prefix="dubbo.protocols." />"
// 1 = {ProtocolConfig@2840} "<dubbo:protocol name="dubbo" port="20881" valid="true" id="dubbo2" prefix="dubbo.protocols." />"
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
ApplicationModel.initProviderModel(pathKey, providerModel);
// 调用此方法进行服务暴露
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
2.3.4 ServiceConfig#doExportUrlsFor1Protocol(1.2.1.2)
org.apache.dubbo.config.ServiceConfig
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
// 获取协议名称,如果是空,默认为 dubbo 协议
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
// 属性覆盖
appendRuntimeParameters(map);
appendParameters(map, metrics);
appendParameters(map, application);
appendParameters(map, module);
// remove 'default.' prefix for configs from ProviderConfig
// appendParameters(map, provider, Constants.DEFAULT_KEY);
appendParameters(map, provider);
appendParameters(map, protocolConfig);
appendParameters(map, this);
...
// export service
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
// 构造一个要导出的 url
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
...
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
// 如果配置了 remote,则表示禁止使用 injvm 协议,就不会进行本地暴露
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (!isOnlyInJvm() && logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (CollectionUtils.isNotEmpty(registryURLs)) {
// 循环所有的 registryURLs
for (URL registryURL : registryURLs) {
...
// 暴露远程服务
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.89.1%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.89.1%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D21564%26qos-port%3D22222%26register%3Dtrue%26release%3D%26side%3Dprovider%26timestamp%3D1577013554161&pid=21564&qos-port=22222®istry=zookeeper×tamp=1577013552772
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
...
}
...
}
}
this.urls.add(url);
}
2.3.5 ServiceConfig#exportLocal(1.2.1.2.1)
org.apache.dubbo.config.ServiceConfig
private void exportLocal(URL url) {
// 改写 url,把协议改成 injvm 协议,host 设置为 127.0.0.1,端口号设置为 0
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
// 本地服务导出
Exporter<?> exporter = protocol.export(
PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
2.3.6 ProtocolFilterWrapper#buildInvokerChain(1.2.1.2.1.1)
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 获取所有激活的 filter Extension
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
// 循环包装
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult;
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return new CallbackRegistrationInvoker<>(last, filters);
}
2.3.7 RegistryProtocol#export(1.2.1.2.2.1)
org.apache.dubbo.registry.integration.RegistryProtocol#export
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// 获取注册中心地址
// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.89.1%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.89.1%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D18816%26qos-port%3D22222%26register%3Dtrue%26release%3D%26side%3Dprovider%26timestamp%3D1577009068166&pid=18816&qos-port=22222×tamp=1577009067710
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
// 获取导出地址
// dubbo://192.168.89.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=org.apache.dubbo.demo.DemoService&bind.ip=192.168.89.1&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=18816&qos-port=22222®ister=true&release=&side=provider×tamp=1577009068166
URL providerUrl = getProviderUrl(originInvoker);
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
// 获取能覆盖配置的订阅地址
// provider://192.168.89.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=org.apache.dubbo.demo.DemoService&bind.ip=192.168.89.1&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=1108&qos-port=22222®ister=true&release=&side=provider×tamp=1577010446361
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
// 添加监听器
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//export invoker
// 调用 originInvoker 的 protocol 实现,进行对应协议的服务暴露,这里是【DubboProtocol】
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
// 获取 registry,这里是 【ZookeeperRegistry】
final Registry registry = getRegistry(originInvoker);
// 简化 url,因为新版本多了配置中心
final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
registryUrl, registeredProviderUrl);
//to judge if we need to delay publish
// 判断是否需要注册
boolean register = registeredProviderUrl.getParameter("register", true);
if (register) {
// 服务注册
register(registryUrl, registeredProviderUrl);
providerInvokerWrapper.setReg(true);
}
// Deprecated! Subscribe to override rules in 2.6.x or before.
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<>(exporter);
}