dubbo消费端初始化源码分析

周末闲来无事,作为程序员只能时不时回味一下之前看过的知识点了,不然容易忘记。记录一下分析dubbo消费端初始化的源码。
我们都知道dubbo是一种rpc框架,配置完善后只需要从spring容器里面取出服务调用的代理对象就可以远程访问服务,这边文章就是分析这个代理对象的初始化过程的。

首先我使用的是基于dubbo2.6.1版本,注册中心是zookeeper。

之前写过一篇服务提供端初始化的源码分析,类似地,消费端的源码入口也是从配置加载开始,dubbo依赖spring提供的扩展方式解析dubbo自定义标签,将配置的属性装配为bean,然后根据配置bean进行一系列初始化。消费端相关配置是ReferenceBean对应着,ReferenceBean初值化完成后因为实现InitializingBean接口所以自动执行afterPropertiesSet,接下来主要初始化调用链是afterPropertiesSet->getObject->get->init->createProxy

其中createProxy返回结果就是远程访问的代理对象。接下来从createProxy开始分析.
createProxy处理之加载注册中心配置信息

//加载注册中心配置,获得的url是仅有注册中心信息,假设是zookeeper注册,格式是zookeeper://xxxxxxxx
List<URL> us = loadRegistries(false);
                if (us != null && us.size() > 0) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        //map中包含配置的reference Service的信息,加入到url中
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }

这里loadRegistries获取到的注册中心,urls.add添加服务信息,可以在后续操作连接到zookeeper获取可用服务的注册信息。

createProxy处理之获取rpc访问代理对象

 List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    //refprotocol.refer返回一个rpc访问代理对象
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; 
                    }
                }
                if (registryURL != null) { 
                    // 设置使用AvailableCluster
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); 
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                }  else { 
                   //无注册中心配置
                    invoker = cluster.join(new StaticDirectory(invokers));
                }

(1)refprotocol.refer(interfaceClass, url)
之前协议一篇dubbospi机制的文章,基于dubbo的spi,分析一下,知道这里调用方法顺序是ProtocolFilterWrapper.refer->ProtocolListenerWrapper.refer->RegistryProtocol.refer,返回一个rpc访问对象,下面是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);
        //registryFactory是适配器,url协议是zookeeper
        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 );
            }
        }
        //cluster是适配器
        return doRefer(cluster, registry, type, url);
    }

registryFactory.getRegistry根据url协议获得zookeeper注册中心实例,然后执行doRefer,返回一个rpc访问对象,下面是代码

    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        //集群目录服务,持有可用服务列表invoker
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        //消费者url,用于注册到zookeeper注册中心comsumer的值,comsumer://xxxxx
        URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
        if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
                && url.getParameter(Constants.REGISTER_KEY, true)) {
            //注册消费者到zookeeper
            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
        //订阅监听节点改变,同时刷新directory服务
        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
                Constants.PROVIDERS_CATEGORY 
                + "," + Constants.CONFIGURATORS_CATEGORY 
                + "," + Constants.ROUTERS_CATEGORY));
        //->MockClusterWrapper.join->FailoverCluster.join
        //返回MockClusterInvoker,持有directory
        return cluster.join(directory);
    }

首先新建一个RegistryDirectory,先简单来说就是这个实例是持有这个服务所有可用服务rpc访问对象的实例,是核心对象。
然后就是构造一个消费者url注册到zookeeper注册中心的comsumer节点上面,表示它是服务的消费者。

再然后就是我们的重点, directory.subscribe方法,这里的作用就是做监听并且刷新directory里面所有可用服务rpc访问对象的列表,这里添加监听分别在
PROVIDERS_CATEGORY,CONFIGURATORS_CATEGORY,ROUTERS_CATEGORY三个节点,也就是[configurators, routers, providers]。下面是代码 directory.subscribe

    public void subscribe(URL url) {
        setConsumerUrl(url);
        registry.subscribe(url, this);
    }

directory持有的registry是zookeeper,下面进入代码是zookeeperRegistry父类FailbackRegistry方法, 在registry.subscribe(url, this)中,调用 doSubscribe(url, listener)这个方法由子类zookeeperRegistry实现,回到ZookeeperRegistry中,下面看ZookeeperRegistry.doSubscribe关键代码

if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
              //......
            } else {
                List<URL> urls = new ArrayList<URL>();
                for (String path : toCategoriesPath(url)) {
                    ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
                    if (listeners == null) {
                        zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
                        listeners = zkListeners.get(url);
                    }
                    ChildListener zkListener = listeners.get(listener);
                    if (zkListener == null) {
                        listeners.putIfAbsent(listener, new ChildListener() {
                            public void childChanged(String parentPath, List<String> currentChilds) {
                            //接收到事件通知,刷新directory的可用服务列表,这里的listener跟踪下来知道是Redirectory实例
                                ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
                            }
                        });
                        zkListener = listeners.get(listener);
                    }
                    zkClient.create(path, false);
                    //path是dubbo/xxxxservice格式,children是获得的可用服务的注册url信息
                    List<String> children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
                //刷新directory可用服务列表
                notify(url, listener, urls);
            }

上面的代码第一件事就是给我们的[configurators, routers, providers]三个节点都加上了事件监听,在加监听的同时获取了节点下面的可用服务的注册值,用来刷新我们listener也就是Redirectory的可用服务列表。最终进入ZookeeperRegistry.notify方法,然后调用ZookeeperRegistry.doNotify方法,然后调用ZookeeperRegistry父类AbstractRegistry的notify方法,代码如下

    protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((urls == null || urls.size() == 0) 
                && ! Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
        }
        Map<String, List<URL>> result = new HashMap<String, List<URL>>();
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
                String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
                List<URL> categoryList = result.get(category);
                if (categoryList == null) {
                    categoryList = new ArrayList<URL>();
                    result.put(category, categoryList);
                }
                //对url参数进行封装
                categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified == null) {
            notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
            categoryNotified = notified.get(url);
        }
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
            saveProperties(url);
            //调用Redirectory的notify刷新其服务列表
            listener.notify(categoryList);
        }
    }

回到了Redirectory的notify方法,关键处是 refreshInvoker(invokerUrls),invokerUrls就是我们的url参数,进入Redirectory的refreshInvoker(invokerUrls)关键代码如下

 Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
            Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
            // state change
            //如果计算错误,则不进行处理.
            if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
                logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
                return ;
            }
            this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
            //设置Redirectory里面的可用服务列表
            this.urlInvokerMap = newUrlInvokerMap;

上面的代码首先根据url创建rpc访问对象,并使用一个map来维护,然后刷新Redirectory里面持有的可用服务rpc访问对象map。
进入toInvokers(invokerUrls)看是如何创建一个rpc访问对象。

if (enabled) {
                        //url格式dubbo,protocol是适配器
                        invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
                    }

上面是toInvokers方法的核心代码,protocol是适配器,所以先执行包装类的refer,最终执行是DubboProtocol的refer方法,代码如下

    public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        // create rpc invoker.
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }

所以,最终在Redirectory持有的invoker格式是:【InvokerDelegete【DubboInvoker】】,而在DubboInvoker里面的doInvoke方法封装了rpc请求的处理。directory.subscribe这一句话就干了这么多事。我们回到先前的doRefer里面,轮到执行cluster.join(directory),这里面cluster是适配器,根据spi分析,方法调用链是MockClusterWrapper.join->FailoverCluster.join

    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new MockClusterInvoker<T>(directory,
                this.cluster.join(directory));
    }
  public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<T>(directory);
    }

那么最终dorefer返回对象是类似这种结构:
【MockClusterInvoker【FailoverClusterInvoker【Redirectory【InvokerDelegete【DubboInvoker】】】】】
回到ReferenceConfig的createProxy方法,接下来执行以下代码invokers,就是上面返回的代理对象。

if (registryURL != null) { // 配置了注册中心协议的URL
                    // AvailableCluster
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); 
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                }  else { // 不是 注册中心的URL
                    invoker = cluster.join(new StaticDirectory(invokers));
                }

使用注册中心,因此执行AvailableCluster,MockClusterWrapper.join->AvailableCluster.join
AvailableCluster.join代码如下

    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {

        return new AbstractClusterInvoker<T>(directory) {
            public Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
                for (Invoker<T> invoker : invokers) {
                    if (invoker.isAvailable()) {
                        return invoker.invoke(invocation);
                    }
                }
                throw new RpcException("No provider available in " + invokers);
            }
        };

    }

获得的invoker 结构变成【MockClusterInvoker【AbstractClusterInvoker【MockClusterInvoker【FailoverClusterInvoker【Redirectory【InvokerDelegete【DubboInvoker】】】】】】】

最终执行

        // 创建服务代理
        return (T) proxyFactory.getProxy(invoker);

根据api分析,getProxy代用链为
StubProxyFactoryWrapper.getProxy->JavassistProxyFactory.getProxy,最后执行到下面这段代码

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

Proxy.getProxy动态生成了一个类,断点查看这里的内容如下:
这里写图片描述
可以见到,类名是proxy0,方法有个sayhi是我们接口中的方法

    public java.lang.String sayhi(java.lang.String arg0) {
        Object[] args = new Object[1];
        args[0] = ($w) $1;
        Object ret = handler.invoke(this, methods[0], args);
        return (java.lang.String) ret;
    }

上面代码中的handler就是new InvokerInvocationHandler(invoker)传入的,结构如下

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler){
        this.invoker = handler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

}

所以InvocationHandler.invoke也算是调用远程方法的入口了。

最后查看一下依赖注入的HelloService是不是proxy0的实例。明显是的
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_20597727/article/details/81154561