深度解析dubbo服务远程引用(创建Proxy流程)

注:本文基于dubbo2.6.1

1.com.alibaba.dubbo.config.ReferenceConfig#createProxy

在《深度解析dubbo服务本地引用(injvm)》一文中,我们分析了dubbo的本地引用,本文接着ReferenceConfig#createProxy 方法下下半部分接着分析。

if (isJvmRefer) {// jvm
 ...
} else {
	
   if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
       ....
   } else { // assemble URL from register center's configuration
       List<URL> us = loadRegistries(false);
       if (us != null && !us.isEmpty()) {
           for (URL u : us) {
               URL monitorUrl = loadMonitor(u);
               if (monitorUrl != null) {// 如果监控url存在的话,就将 monitor 塞到map中
                   map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
               }/// 将url添加到urls, refer= k=v&k1=v1&k2=v2
               urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
           }
       }
       if (urls.isEmpty()) {// urls
           throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
       }
   }

   if (urls.size() == 1) {
       invoker = refprotocol.refer(interfaceClass, urls.get(0));
   } else {
       List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
       URL registryURL = null;
       for (URL url : urls) {
           invokers.add(refprotocol.refer(interfaceClass, url));
           if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
               registryURL = url; // use last registry url
           }
       }
       if (registryURL != null) { // registry url is available
           // use AvailableCluster only when register's cluster is available
           URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
           invoker = cluster.join(new StaticDirectory(u, invokers));
       } else { // not a registry url
           invoker = cluster.join(new StaticDirectory(invokers));
       }
   }
}

这里我们就找着重要的说,分为两个大部分,首先是获取url,将获取的url添加到urls,接着就是判断urls的size,如果是一个的话
直接调用 refprotocol.refer(interfaceClass, urls) 获得invoker ,如果是多个的话就会循环调用然后塞到invokers,之后将多个invoker 进行join合并成一个。
下面我们就看下 refprotocol.refer(interfaceClass, urls) 这个是怎样生成invoker的。
这里这个refprotocol是 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
是Protocol接口自适应类,这里我们protocol是registry,所以这个refprotocol.refer其实最后就调到了RegistryProtocol。
下面我们看下com.alibaba.dubbo.registry.integration.RegistryProtocol#refer方法。

2.com.alibaba.dubbo.registry.integration.RegistryProtocol#refer

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        return doRefer(cluster, registry, type, url);
    }

第一行是将url 中parameters 里面的registry的值获取出来,如果你是用zk作为注册中心,获取出来的就是zookeeper,然后设置到url的protocol属性上,之后就是将parameters 的 registry remove掉了,这时候url的protocol 就是zookeeper了
第二行 就是根据 url的protocol来获取RegistryFactory 的实现类,也就是ZookeeperRegistryFactory ,然后就是调用它的getRegistry()方法,获取Registry ,总而言之就是获取 注册中心的Registry 对象。
接着就是如果你这个接口类型是RegistryService 的话,就找proxyFactory 来生成invoker。
再往下走就是解析将url中refer 属性值取出来,然后解析成map,其实这个refer就是在ReferenceConfig#createProxy 中塞进去的,就是一些属性,然后获取group 属性,进行分组。走到最后就是调用了doRefer()方法,我么看下这个doRefer方法。

3.com.alibaba.dubbo.registry.integration.RegistryProtocol#doRefer

 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {

        //创建Directory 对象
        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(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            URL registeredConsumerUrl = getRegisteredConsumerUrl(subscribeUrl, url);
            registry.register(registeredConsumerUrl);// 注册
            directory.setRegisteredConsumerUrl(registeredConsumerUrl);
        }
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
                Constants.PROVIDERS_CATEGORY
                        + "," + Constants.CONFIGURATORS_CATEGORY
                        + "," + Constants.ROUTERS_CATEGORY));
        //创建invoker对象
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

首先是创建了个RegistryDirectory 对象,这个对象非常重要,Directory 是目录的意思,而这里就是多个invoker,这里维护了invoker列表,而且这个列表是会根据注册中心变动的。
接着就是将registry与protocol设置进去,再往下就是生成一个subscribeUrl。如果subscribeUrl 的ServiceInterface不是* ,register属性是true,这时候生成一个registeredConsumerUrl,进行注册,然后将registeredConsumerUrl 设置到directory 的consumerUrl属性上。
接着就是调用 directory.subscribe 的方法进行订阅,之后就是 cluster.join(directory); 获取invoker ,这个cluster 是dubbo spi注入进来的,
我们看下这个Cluster的定义:

@SPI(FailoverCluster.NAME)  //默认是failover 失败重试
public interface Cluster {
    /**
     * Merge the directory invokers to a virtual invoker.
     * 基于Directory 对象创建invoker对象
     * @param <T>
     * @param directory
     * @return cluster invoker
     * @throws RpcException
     */
    @Adaptive  //基于 Dubbo SPI Adaptive 机制,加载对应的 Cluster 实现,使用 URL.cluster 属性
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

}

这里用户没有特殊配置的话,就是使用FailoverCluster 子类的join方法。但是使用dubbo spi的时候,还有包装机制,会将FailoverCluster对象外层包上一个MockClusterWrapper,所以我们需要先看下MockClusterWrapper

public class MockClusterWrapper implements Cluster {
	// 这个cluster才是真正的 FailoverCluster
    private Cluster cluster;
    public MockClusterWrapper(Cluster cluster) {
        this.cluster = cluster;
    }
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory));
    }
}

我们看到先是调用了cluster.join()方法,然后将返回的invoker对象用MockClusterInvoker 包了一层,这里这个cluster 才是真正的FailoverCluster对象,我们来看看它干了啥。

public class FailoverCluster implements Cluster {
    public final static String NAME = "failover";
    // 实现接口join方法 ,创建 对应invoker处理
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<T>(directory); /// new 一个失败重试的invoker
    }
}

我们可以看到new FailoverClusterInvoker 对象返回了,这个FailoverClusterInvoker 其实是一个失败重试invoker。
我们一会在来看FailoverClusterInvoker 内部实现机制,因为我们现在已经得到了invoker对象,现在我们一层一层返回去,这时候我们发现退回到了最初com.alibaba.dubbo.config.ReferenceConfig#createProxy方法。
在这里插入图片描述
上面这个红框框起来的是我们刚才调用的,这是urls就一个是情况,多个的url的时候我们看到是循环来获得invoker,然后取得最后一个invoker赋值给registryURL,最后将多个invoker合成了一个。接着再往下走:
在这里插入图片描述
接着就是是否需要检查invoker的可用性,当check=false的时候不会检查,默认是true的,如果没有可以使用的provider就会抛出异常,这个错误应该常见的。
接着就是创建 proxy代理类了,我们在《深度解析dubbo服务本地引用(injvm)》中讲过这部分内容,我们再来回顾下这个proxy生成是什么样子的。

4. Proxy代理类生成过程

(T) proxyFactory.getProxy(invoker)

我们先来看看这个ProxyFactory这个接口,三个抽象方法,这个getInvoker方法我们在服务暴露那几篇已经探讨过了,根据dubbo spi 的自适应规则,先去找url中PROXY_KEY 的值,如果有值的话使用我们自定义的,如果没有值的话就使用@SPI注解里面实现类JavassistProxyFactory。

@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker) throws RpcException;
    // 是否范化调用
    @Adaptive({Constants.PROXY_KEY})
    <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}

JavassistProxyFactory 继承父类AbstractProxyFactory ,然后这个两个getProxy方法都是在父类实现的
在这里插入图片描述
这个一个参数的getProxy调用两个参数的getProxy,然后范化参数是false,表示不范化调用。
在这里插入图片描述
我们可以看到先从invoker的url里面获取interfaces,这个一看就就是多个interface的,我们这里是null,直接走了是null的情况,将我们自己业务接口class 与EchoService class 放到了interfaces中,下面那个是范化调用的封装,我们暂时先不管它,接着就是调用子类的getProxy(invoker,Class<?>[] types) 方法。
在这里插入图片描述
在这里插入图片描述
我们可以直接看下生成的proxy代码。
这里我把上篇文章里的截图拿过来了。
在这里插入图片描述
我们看到在方法调用的时候 其实是调用的invoker中的invoke方法,由这个invoker来帮你处理,这篇主要是讲了Proxy代理创建的大体流程,然后最后得到我们在业务调用方法的时候其实是调用的invoker的invoke方法,我们在下篇主要是讲下这个invoker 里面的invoke方法是怎样子执行的。

猜你喜欢

转载自blog.csdn.net/yuanshangshenghuo/article/details/107215252