dubbo源码分析——服务引用过程

先有个脑图:

直接来到ReferenceBean:

调试的时候如果进不来init,可以这样:

com.alibaba.dubbo.config.ReferenceConfig#init :

检查配置然后将配置构建成 map 

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

判断是injvm还是直连还是注册中心模式:

如果是走本地的话,那么直接构建个走本地协议的 URL 然后进行服务的引入,即 refprotocol.refer,这个方法之后会做分析,本地的引入就不深入了,就是去之前服务暴露的 exporterMap 拿到服务

如果不是本地,那肯定是远程了,接下来就是判断是点对点直连 provider 还是通过注册中心拿到 provider 信息再连接 provider 了,我们分析一下配置了 url 的情况,如果配置了 url 那么不是直连的地址,就是注册中心的地址。

然后就是没配置 url 的情况,到这里肯定走的就是注册中心引入远程服务了。

List<URL> us = this.loadRegistries(false);//拿到注册中心
    if (us != null && !us.isEmpty()) {
          for(Iterator var5 = us.iterator(); var5.hasNext(); this.urls.add(u.addParameterAndEncoded("refer", StringUtils.toQueryString(map)))) {
          u = (URL)var5.next();
          url = this.loadMonitor(u);//监控中心
          if (url != null) {
           map.put("monitor", URL.encode(url.toFullString()));
       }
    }
 }

if (this.urls.isEmpty()) {
        throw new IllegalStateException("No such any registry to reference " + this.interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}

if (this.urls.size() == 1) {
      this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
  } else {

        ....
}

urls:

简述一下就是先检查配置,通过配置构建一个 map ,然后利用 map 来构建 URL ,再通过 URL 上的协议利用自适应扩展机制调用对应的 protocol.refer 得到相应的 invoker 。

在有多个 URL 的时候,先遍历构建出 invoker 然后再由 StaticDirectory 封装一下,然后通过 cluster 进行合并,只暴露出一个 invoker 。

然后再构建代理,封装 invoker 返回服务引用,之后 Comsumer 调用的就是这个代理类。

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

   public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = url.setProtocol(url.getParameter("registry", "dubbo")).removeParameter("registry");
        Registry registry = this.registryFactory.getRegistry(url);//获取注册中心
        if (RegistryService.class.equals(type)) {
            return this.proxyFactory.getInvoker(registry, type, url);
        } else {
            Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded("refer"));
            String group = (String)qs.get("group");
            return group == null || group.length() <= 0 || Constants.COMMA_SPLIT_PATTERN.split(group).length <= 1 && !"*".equals(group) ? this.doRefer(this.cluster, registry, type, url) : this.doRefer(this.getMergeableCluster(), registry, type, url);
        }
    }

主要就是获取注册中心实例,然后调用 doRefer 进行真正的 refer

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

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory(type, url);
        directory.setRegistry(registry);//塞入注册中心实例
        directory.setProtocol(this.protocol);//塞入动态生成的protocol
        Map<String, String> parameters = new HashMap(directory.getUrl().getParameters());
        //生成服务消费者链接
        URL subscribeUrl = new URL("consumer", (String)parameters.remove("register.ip"), 0, type.getName(), parameters);
        if (!"*".equals(url.getServiceInterface()) && url.getParameter("register", true)) {
            //向注册中心注册服务消费者,在consumers目录下创建新节点
            registry.register(subscribeUrl.addParameters(new String[]{"category", "consumers", "check", String.valueOf(false)}));
        }
        //在订阅注册中心的providers、configurators、routers目录,订阅完就会触发DubboProtocol的refer方法
        directory.subscribe(subscribeUrl.addParameter("category", "providers,configurators,routers"));
        //封装多个invoker
        Invoker invoker = cluster.join(directory);
        //记录到注册表
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

这个方法很关键,可以看到生成了RegistryDirectory 这个 directory 并塞入了注册中心实例,它自身也实现了NotifyListener 接口,因此注册中心的监听其实是靠这家伙来处理的

然后向注册中心注册自身的信息,并且向注册中心订阅了 providers 节点、 configurators 节点 和 routers 节点,订阅了之后 RegistryDirectory 会收到这几个节点下的信息,就会触发 DubboInvoker 的生成了,即用于远程调用的 Invoker

然后通过 cluster 再包装一下得到 Invoker,因此一个服务可能有多个提供者,最终在 ProviderConsumerRegTable 中记录这些信息,然后返回 Invoker。

所以我们知道Conusmer 是在 RegistryProtocol#refer 中向注册中心注册自己的信息,并且订阅 Provider 和配置的一些相关信息。

拿到了Provider的信息之后就可以通过监听触发 DubboProtocol# refer 了(具体调用哪个 protocol 还是得看 URL的协议的,我们这里是 dubbo 协议),整个触发流程我就不一一跟一下了,看下调用栈就清楚了

public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        this.optimizeSerialization(url);
        //拿到接口类,远程服务的url,然后创建一个client
        DubboInvoker<T> invoker = new DubboInvoker(serviceType, url, this.getClients(url), this.invokers);
        this.invokers.add(invoker);
        return invoker;
    }

invoker:

调用栈: 

这里的重点在 getClients,因为终究是要跟远程服务进行网络调用的,而 getClients 就是用于获取客户端实例,实例类型为 ExchangeClient,底层依赖 Netty 来进行网络通信,并且可以看到默认是共享连接。

    private ExchangeClient[] getClients(URL url) {
        boolean service_share_connect = false;
        //获取连接数,默认为0
        int connections = url.getParameter("connections", 0);
        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] = this.getSharedClient(url);//返回共享的客户端
            } else {
                clients[i] = this.initClient(url);//得到初始化的新的客户端
            }
        }

        return clients;
    }

//通过远程地址找 client ,这个 client 还有引用计数的功能,如果该远程地址还没有 client 则调用 initClient
private ExchangeClient getSharedClient(URL url) {
        String key = url.getAddress();
        ReferenceCountExchangeClient client = (ReferenceCountExchangeClient)this.referenceClientMap.get(key);
        if (client != null) {
            if (!client.isClosed()) {
                client.incrementAndGetCount();
                return client;
            }

            this.referenceClientMap.remove(key);
        }

        this.locks.putIfAbsent(key, new Object());
        synchronized(this.locks.get(key)) {
            if (this.referenceClientMap.containsKey(key)) {
                return (ExchangeClient)this.referenceClientMap.get(key);
            } else {
                ExchangeClient exchangeClient = this.initClient(url);
                client = new ReferenceCountExchangeClient(exchangeClient, this.ghostClientMap);
                this.referenceClientMap.put(key, client);
                this.ghostClientMap.remove(key);
                this.locks.remove(key);
                return client;
            }
        }
    }

private ExchangeClient initClient(URL url) {
        //获取客户端类型,默认为netty
        String str = url.getParameter("client", url.getParameter("server", "netty"));
        url = url.addParameter("codec", "dubbo");
        url = url.addParameterIfAbsent("heartbeat", String.valueOf(60000));
        if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
            throw new RpcException("Unsupported client type: " + str + ", supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
        } else {
            try {
                Object client;
                if (url.getParameter("lazy", false)) {
                    client = new LazyConnectExchangeClient(url, this.requestHandler);
                } else {
                    client = Exchangers.connect(url, this.requestHandler);
                }

                return (ExchangeClient)client;
            } catch (RemotingException var5) {
                throw new RpcException("Fail to create remoting client for service(" + url + "): " + var5.getMessage(), var5);
            }
        }
    }

然后最终得到的 Invoker就是这个样子,就是下面截图的样子:

com.alibaba.dubbo.registry.integration.RegistryProtocol#doRefer返回的

最终将调用 return (T) proxyFactory.getProxy(invoker); 返回一个代理对象,这个就不做分析了。

   private T createProxy(Map<String, String> map) {
       ...

            if (this.urls.size() == 1) {
                this.invoker = refprotocol.refer(this.interfaceClass, (URL)this.urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList();
                ...
            }
        }

      ..
            return proxyFactory.getProxy(this.invoker);
        }
    }

流程总结:

本文参考:

https://www.cnblogs.com/aobing/p/13630666.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值