21. Dubbo服务引入 - 创建代理对象
1. 开篇
本文介绍Dubbo服务引入部分的源码分析——创建代理对象。
2. 创建代理
该方法的主要目的是,创建Invoker
代理对象,创建代理对象的方法在ReferenceConfig#createProxy
中。
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) {
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
} else {
// .....省略代码
if (urls.size() == 1) {
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (UrlUtils.isRegistry(url)) {
registryURL = url;
}
}
if (registryURL != null) {
String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else {
String cluster = CollectionUtils.isNotEmpty(invokers)?
(invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT): Cluster.DEFAULT;
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
}
}
}
URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
MetadataUtils.publishServiceDefinition(consumerURL);
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
该方法主要处理流程如下:
- 根据map判断是否为
injvm
引用, 如果是则使用injvm
的服务。 - 如果
@DubboReference
注解中设置了url
参数,则进行解析并将其放入urls
中,url
参数可以直接写服务提供者的地址,这样可以直接进行调用。 - 判断协议如果不是
injvm
协议,则进行检测注册中心,去除注册中心的URL并添加refer
参数,并将url
放入urls
中。 - 判断
urls
的个数,如果只有一个,则直接调用REF_PROTOCOL.refer
返回代理对象。 - 如果
urls
内有多个,则遍历每一个url,调用REF_PROTOCOL.refer
生成代理对象。 - 判断是否存在注册中心URL配置。
- 如果存在,则把Invoker集合整合为
ZoneAwareClusterInvoker
。 - 如果不存在,则把Invoker集合整合为
FailoverClusterInvoker
。 - 构造consumerURL。
- 调用
PROXY_FACTORY.getProxy
方法,生成代理对象,其中PROXY_FACTORY
是个代理工厂,默认使用的是javassist
。
3. refer
REF_PROTOCOL.refer
方法会生成一个Invoker
对象。REF_PROTOCOL
是Protocol
接口的一个自适应代理类。
根据urls.get(0)
来判断,REF_PROTOCOL
对象的值为RegistryProtocol
,当然因为Protocol
的实现类有Wrapper
类,RegistryProtocol
会被Wrapper
类包裹一层,这个Wrapper类为ProtocolFilterWrapper
和ProtocolListenerWrapper
。
ProtocolListenerWrapper
类作用为,当在dubbo
获取到Invoker
之后,我们可以设置一些回调,通过DubboSPI
获取InvokerListener
实例,可以对Invoker
对象做一些处理工作。
ProtocolFilterWrapper
类主要作用为,通过Dubbo SPI
获取Filter
的所有实现类,并将其串联起来,最终返回的是一个FilterNode
。
下面来看一下RegistryProtocol
的refer
方法。
该方法处理逻辑如下:
- 根据
url
来获取注册中心。 - 从
url
中获取refer
对应的值,这个值代表引用的服务。 - 根据服务参数中的分组信息,来判断使用哪一个
Cluster
。 - 如果有多个分组,或者分组为
*
,则使用MergeableClusterInvoker
。 - 如果没有指定分组,则使用
FailoverClusterInvoker
。 - 最后,调用
doRefer
方法创建Invoker
。
url
中获取refer
的值示例如下:
application=dubbo-demo-annotation-consumer&dubbo=2.0.2&init=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=3023®ister.ip=172.17.0.157&side=consumer&sticky=false×tamp=1671457001297
4. doRefer
在doRefer
方法中,会通过Dubbo SPI
获取RegistryProtocolListener
的实现类,然后对实现类,进行循环调用onRefer
方法,最终会返回Invoker
对象。
这里也会处理一下服务的监听,这一块后面再进行介绍。
5. join
String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME);
invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
通过Cluster
的join
方法,来将多个invokers
合并成一个invoker
。
Cluster
是一个接口,通过Dubbo SPI
进行加载具体的实例,从代码中,我们可以看到Cluster
的默认实现为failover
。但是在registryURL
获取cluster
时,判断如果不存在的话,就是用zone-aware
,所以这里获取到的是ZoneAwareCluster
。
当有多个invoker
时,在调用的时候,会循环判断invoker
是否可用,如果可用的话才会执行调用。
6. 后记
本文介绍Dubbo服务引入部分的源码分析,创建代理对象的过程以及源码分析,下篇将介绍服务目录的监听配置。