Dubbo源码|二十一、Dubbo服务引入——创建代理对象
开篇
本文介绍Dubbo服务引入部分的源码分析——创建代理对象。
创建代理
该方法的主要目的是,创建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。
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
doRefer
在doRefer方法中,会通过Dubbo SPI获取RegistryProtocolListener的实现类,然后对实现类,进行循环调用onRefer方法,最终会返回Invoker对象。
这里也会处理一下服务的监听,这一块后面再进行介绍。
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是否可用,如果可用的话才会执行调用。
后记
本文介绍Dubbo服务引入部分的源码分析,创建代理对象的过程以及源码分析,下篇将介绍服务目录的监听配置。
转载自:https://juejin.cn/post/7181676071090126885




