dubbo服务引用初始化浅析

dubbo服务引用初始化过程就是创建服务动态代理的过程,与服务发布一样,同样借助bean初始化完成动态代理的创建。具体调用过程:
com.alibaba.dubbo.config.ReferenceConfig:
get()-->init()-->createProxy()
在createProxy()方法中,服务引用分为三种情况:
1.JVM内部引用的代理
2.用户指定URL 直连
3.通过注册中心,引用远程的代理
直接看第三种

private T createProxy(Map map) {
         .....................................
                List us = loadRegistries(false);//导入注册中心url
                if (us != null && us.size() > 0) {
                    for (URL u : us) {
                        URL monitorUrl = loadMonitor(u);//导入monitor相关信息,加入URL中
                        if (monitorUrl != null) {
                            map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                        }
                        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                    }
                }
                if (urls == null || urls.size() == 0) {
                    throw new IllegalStateException("No such any registry to reference " + interfaceName  + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config  to your spring config.");
                }
            }

            if (urls.size() == 1) {//只有一个注册中心
                invoker = refprotocol.refer(interfaceClass, urls.get(0));
            } else {//多注册中心
                List> invokers = new ArrayList>();
                URL registryURL = null;
                for (URL url : urls) {//每个注册中心初始化引用一遍
                    invokers.add(refprotocol.refer(interfaceClass, url));
                    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // 用了最后一个registry url
                    }
                }
                if (registryURL != null) { // 有 注册中心协议的URL
                    // 对有注册中心的Cluster 只用 AvailableCluster
                    // 多个注册中心,封装成一个Invoker
                    URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME); 
                    invoker = cluster.join(new StaticDirectory(u, invokers));
                }  else { // 不是 注册中心的URL
                    invoker = cluster.join(new StaticDirectory(invokers));
                }
            }
        }
    .....................................
        // 创建服务代理
        return (T) proxyFactory.getProxy(invoker);
    }

主要服务引用过程在这个方法里refprotocol.refer(interfaceClass, url),调用到RegistryProtocol.refer(),根据url获取到注册中心,然后调用doRefer(),new 一个RegistryDirectory,主要缓存远程服务相关信息,如ip:port,然后向注册中心consumer端注册自己,然后在相应节点providers端订阅提供者的信息,具体过程如下图


dubbo服务引用初始化浅析_第1张图片
clipboard.png
private  Invoker doRefer(Cluster cluster, Registry registry, Class type, URL url) {
        RegistryDirectory directory = new RegistryDirectory(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        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)) {//注册自己
              registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
                    Constants.CHECK_KEY, String.valueOf(false)));
        }
      //订阅provider注册中心节点信息,此处url需要携带providers参数
       directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
                Constants.PROVIDERS_CATEGORY 
                + "," + Constants.CONFIGURATORS_CATEGORY 
                + "," + Constants.ROUTERS_CATEGORY));
        return cluster.join(directory);
    }

下面进入directory.subscribe()方法,此方法非常重要,此处完成提供者信息的获取,通信客户端的初始化,此方法调用链为:FailbackRegistry.subscribe()-->ZookeeperRegistry.doSubscribe()-->RegistryDirectory.notify(),下面来看ZookeeperRegistry.doSubscribe()方法,此处主要完成提供端信息的获取,转换成url,订阅的节点有三类:
/dubbo/xx.xx/providers;
/dubbo/xx.xx/routes;
/dubbo/xx.xx/configurations;

protected void doSubscribe(final URL url, final NotifyListener listener) {
     List urls = new ArrayList();//缓存订阅到信息
      for (String path : toCategoriesPath(url)) {
              ConcurrentMap listeners = zkListeners.get(url);
                  ...............................................
                    zkClient.create(path, false);
                   //从zk订阅到的信息,并添加子节点监听
                    List children = zkClient.addChildListener(path, zkListener);
                    if (children != null) {
                        urls.addAll(toUrlsWithEmpty(url, path, children));
                    }
                }
            notify(url, listener, urls);
  }

然后调用到AbstractRegistry.notify()方法

protected void notify(URL url, NotifyListener listener, List urls) {
        ..........................................
        for (Map.Entry> entry : result.entrySet()) {
            String category = entry.getKey();
            List categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
            saveProperties(url);//url信息保存到本地
            listener.notify(categoryList);
        }
}

listener就是一路传过来的RegistryDirectory,进入RegistryDirectory.notify()-->refreshInvoker()

private void refreshInvoker(List invokerUrls){
           ..........................................
            Map> newUrlInvokerMap = toInvokers(invokerUrls) ;// 将URL列表转成Invoker列表
            Map>> 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;
            this.urlInvokerMap = newUrlInvokerMap;
            try{
                destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 关闭未使用的Invoker
            }catch (Exception e) {
                logger.warn("destroyUnusedInvokers error. ", e);
            }
}

上面的方法提供者url信息转换成invoker,并与method对应起来。下面进入toInvokers(invokerUrls):

 private Map> toInvokers(List urls) {
          //检查提供者相关信息.......
         ....................
          // 缓存key为没有合并消费端参数的URL,不管消费端如何合并参数,如果服务端URL发生变化,则重新refer
            Map> localUrlInvokerMap = this.urlInvokerMap; // local reference
            Invoker invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
            if (invoker == null) { // 缓存中没有,重新refer
                try {
                    ........................................
                    if (enabled) {
                        invoker = new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);
                    }
                } catch (Throwable t) {
                    logger.error("Failed to refer invoker for interface:"+serviceType+",url:("+url+")" + t.getMessage(), t);
                }
                if (invoker != null) { // 将新的引用放入缓存
                    newUrlInvokerMap.put(key, invoker);
                }
            }else {
                newUrlInvokerMap.put(key, invoker);
            }
        }
        keys.clear();
        return newUrlInvokerMap;
}

下面调用protocol.refer(serviceType, url),此protocol为适配类,调用过程为ProtocolFilterWrapper.refer()-->ProtocolListenerWrapper.refer()-->DubboProtocol.refer():

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

然后调用getClients(url),创建ExchangeClient用来处理和提供端的链接,一般一个服务接口只会创建一个client

private ExchangeClient[] getClients(URL url){
        //是否共享连接
        boolean service_share_connect = false;
        int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
        //如果connections不配置,则共享连接,否则每服务每连接
        if (connections == 0){
            service_share_connect = true;
            connections = 1;
        }
        ExchangeClient[] clients = new ExchangeClient[connections];
        for (int i = 0; i < clients.length; i++) {
            if (service_share_connect){
                clients[i] = getSharedClient(url);
            } else {
                clients[i] = initClient(url);
            }
        }
        return clients;
    }

然后一路调用
Exchangers.connect()-->HeaderExchanger.connect()->Transporters.connect()-->NettyTransporter.connect(),最终初始化一个NettyClient,调用doopen()方法,完成网络客户端的初始化

    protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        bootstrap = new ClientBootstrap(channelFactory);
        // config
        // @see org.jboss.netty.channel.socket.SocketChannelConfig
        bootstrap.setOption("keepAlive", true);
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("connectTimeoutMillis", getTimeout());
        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
    }

回到RegistryProtocol.doRefer()中,cluster.join(directory),此处调用过程为:MockClusterWrapper.join()-->FailfastCluster.join(),最终初始化一个MockClusterInvoker对象,此对象持有RegistryDirectory和FailfastClusterInvoker的引用,接下来调用JavassistProxyFactory.getProxy()创建代理:

 public  T getProxy(Invoker invoker, Class[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

此对象spring注入服务接口中,形成一个透明化的本地服务,当调用本地接口方法时,就会启动动态代理,执行InvokerInvocationHandler.invoke()方法:

 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]);
        }
        //调用MockClusterInvoker.invoke()方法
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }

你可能感兴趣的:(dubbo服务引用初始化浅析)