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
- 由于
ReferenceBean
实现了FactoryBean
接口,所以会在getObject
方法中返回真实的 Spring Bean 对象
2.2 流程预览
// 1. dubbo 与 Spring 集成,ReferenceBean 继承了 FactoryBean 接口,所以会调用 【getObject】 方法获取实现类
org.apache.dubbo.config.spring.ReferenceBean#getObject ->
org.apache.dubbo.config.ReferenceConfig#get
// 1.1 检查和更新属性,同之前步骤
org.apache.dubbo.config.ReferenceConfig#checkAndUpdateSubConfigs
// 1.2 初始化 ref 对象
org.apache.dubbo.config.ReferenceConfig#init ->
// 1.2.1(*) 创建代理对象
org.apache.dubbo.config.ReferenceConfig#createProxy
// 1.2.1.1 判断是否是 jvm 引用,判断依据就是:InjvmProtocol 中的 【exporterMap】 中是否有 url 所对应的 serviceKey
// serviceKey:接口名 + 方法名 + version
org.apache.dubbo.config.ReferenceConfig#shouldJvmRefer
// 1.2.1.2 如果配置了 url(直连服务提供者),则会解析 url,添加到 urls 中,因为可以用 ; 号隔开添加多个,注意这里也可以配置 registry 开头的服务直连
// 1.2.1.3 获取注册中心地址,构造 urls 对象(存放所有的 ),urls 结构如:
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272
// &qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService
// %26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer
// %26sticky%3Dfalse%26timestamp%3D1577587535345®istry=zookeeper×tamp=1577587537916
// 1.2.1.4(*) 调用 refer 方法创建 invoker
// 调用 Protocol 的动态代理类 【Protocol$Adaptive】 的 refer 方法,先经过 wrapper 包装类,再到 【RegistryProtocol】
org.apache.dubbo.rpc.Protocol$Adaptive#refer ->
// 当 protocol 不为 registry 时才会起作用,这里是 registry,所以没有作用
org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper#refer ->
// 当 protocol 不为 registry 时才会起作用,这里是 registry,所以没有作用
org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper#refer ->
// 1.2.1.4.1 把 registry 协议改为 url 中 registry 所对应的协议,这里 zookeeper 协议,默认是 dubbo 协议,形如
// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19048
// &qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService
// %26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19048%26qos-port%3D33333%26register.ip%3D192.168.89.1
// %26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577590285415×tamp=1577590287612
org.apache.dubbo.registry.integration.RegistryProtocol#refer
// 1.2.1.4.1.1(*) 最终要的步骤
org.apache.dubbo.registry.integration.RegistryProtocol#doRefer
// 1.2.1.4.1.1.1 创建服务目录
org.apache.dubbo.registry.integration.RegistryDirectory#<init>
// 1.2.1.4.1.1.2 向 zk 注册服务消费者地址
org.apache.dubbo.registry.RegistryService#register
// 1.2.1.4.1.1.3 创建路由链
org.apache.dubbo.registry.integration.RegistryDirectory#buildRouterChain ->
// 创建路由链,然后设置到 RegistryDirectory 中
org.apache.dubbo.rpc.cluster.RouterChain#buildChain
// 1.2.1.4.1.1.3.1(*) 获取所有 @Activate 注解 RouterFactory 的扩展类,创建路由集合 routers,根据优先级排序
org.apache.dubbo.rpc.cluster.RouterChain#<init>
// 1.2.1.4.1.1.4 监听 providers、configurators、routers 三个目录
org.apache.dubbo.registry.integration.RegistryDirectory#subscribe
// 1.2.1.4.1.1.5 调用 Cluster 的动态代理类 【Cluster$Adaptive】 的 join 方法创建 invoker 对象,先经过 wrapper 包装类,再到 【FailoverCluster】
org.apache.dubbo.rpc.cluster.Cluster$Adaptive#join ->
// 创建 MockClusterInvoker,用于 在 invoker 中判断是否需要 mock
org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper#join ->
// 创建 FailoverClusterInvoker 对象,持有 RegistryDirectory
org.apache.dubbo.rpc.cluster.support.FailoverCluster#join
// 1.2.1.5 调用 ProxyFactory 的动态代理类 【ProxyFactory$Adaptive】 的 getProxy 方法创建动态代理对象,先经过 wrapper 包装类,再到 【JavassistProxyFactory】
org.apache.dubbo.rpc.ProxyFactory$Adaptive#getProxy ->
// 用于实现本地存根 stub 逻辑
org.apache.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper#getProxy ->
// 创建代理对象
org.apache.dubbo.rpc.proxy.AbstractProxyFactory#getProxy ->
// 使用 Javassist 技术创建动态代理对象,和 jdk 基本一致,InvocationHandler 是 dubbo 中的 【InvokerInvocationHandler】
org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory#getProxy
2.3 流程详解
2.3.1 ReferenceConfig#createProxy(1.2.1)
org.apache.dubbo.config.ReferenceConfig
private T createProxy(Map<String, String> map) {
// 判断是否是 jvm 引用,判断依据就是:InjvmProtocol 中的 【exporterMap】 中是否有 url 所对应的 serviceKey
if (shouldJvmRefer(map)) {
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url);
...
}
// 如果配置了 url(直连服务提供者),则会解析 url,添加到 urls 中,因为可以用 ; 号隔开添加多个
else {
urls.clear(); // reference retry init will add url to urls, lead to OOM
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
// 也可以配置 registry 开头的服务直连
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
}
// 创建 invoker 对象
else { // assemble URL from register center's configuration
...
}
// 如果只有一个注册中心地址,直接取第一个调用 refer 方法创建 invoker
// urls 中存放的对象是:
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272&qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577587535345®istry=zookeeper×tamp=1577587537916
if (urls.size() == 1) {
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
}
// 多个注册中心处理逻辑
else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
// 遍历所有的注册中心地址,把多个 invoker 合并成一个 invoker 对象
for (URL url : urls) {
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// use RegistryAwareCluster only when register's CLUSTER is available
URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
// The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
invoker = CLUSTER.join(new StaticDirectory(u, invokers));
} else { // not a registry url, must be direct invoke.
invoker = CLUSTER.join(new StaticDirectory(invokers));
}
}
}
...
// create service proxy
// 调用 ProxyFactory 的动态代理类 【ProxyFactory$Adaptive】 的 getProxy 方法创建动态代理对象,先经过 wrapper 包装类,再到 【JavassistProxyFactory】
return (T) PROXY_FACTORY.getProxy(invoker);
}
2.3.2 RegistryProtocol#refer(1.2.1.4)
org.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 把 registry 协议改为 url 中 registry 所对应的协议,这里 zookeeper 协议,默认是 dubbo 协议
// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&pid=19272&qos-port=33333&refer=application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.2%26interface%3Dorg.apache.dubbo.demo.DemoService%26lazy%3Dfalse%26methods%3DsayHello%26pid%3D19272%26qos-port%3D33333%26register.ip%3D192.168.89.1%26side%3Dconsumer%26sticky%3Dfalse%26timestamp%3D1577587535345×tamp=1577587537916
url = URLBuilder.from(url)
.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY)
.build();
// 因为这里的协议已经变成了 zookeeper,所以这里的 registry 是 ZookeeperRegistry
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group 处理
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));
String group = qs.get(GROUP_KEY);
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
return doRefer(cluster, registry, type, url);
}
2.3.3 RegistryProtocol#doRefer(1.2.1)
org.apache.dubbo.registry.integration.RegistryProtocol#doRefer
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// 创建服务目录
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
// 向 zk 注册服务消费者地址,即创建 consumer 临时节点
registry.register(directory.getRegisteredConsumerUrl());
}
// 创建路由链
directory.buildRouterChain(subscribeUrl);
// 监听 providers、configurators、routers 三个目录
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
// 调用 Cluster 的动态代理类 【Cluster$Adaptive】 的 join 方法创建 invoker 对象,先经过 wrapper 包装类,再到 【FailoverCluster】
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
2.3.4 RouterChain#(1.2.1.4.1.1.3.1)
org.apache.dubbo.rpc.cluster.RouterChain#RouterChain
private RouterChain(URL url) {
// 获取所有 @Activate 注解 RouterFactory 的扩展类
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getActivateExtension(url, (String[]) null);
// 循环 RouterFactory,分别调用 getRoute 方法存储到集合中,这里有 4 个 route
// 0 = {MockInvokersSelector@3011}
// 1 = {TagRouter@3012}
// 2 = {AppRouter@3013}
// 3 = {ServiceRouter@3014}
List<Router> routers = extensionFactories.stream()
.map(factory -> factory.getRouter(url))
.collect(Collectors.toList());
initWithRouters(routers);
}