Dubbo源码解读-Exchangeclient初始化

上篇我们介绍了Consumer消费端调用流程解析,地址如下Dubbo源码解读-Consumer调用流程解析-CSDN博客

        本文主要针Dubbo消费端Exchange初始化过程,从dubbo源码角度进行解析。

        大家可以好好仔细读一下本文。有疑问欢迎留言。

        接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。

         不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。

        如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。

主要内容

  • ExchangeClient初始化背景知识
  • ExchangeClient执行链
  • ExchangeClient初始化源码解析
  • 图解ExchangeClient调用链。

ExchangeClient初始化背景知识

ExchangeClient初始化时机

        消费端服务列表刷新时,RegistryDirectroy类实现NotifyListener接口,监听注册中心节点。当消费者启动或者服务列表发生变动时候,监听事件会包装providers节点写的服务,创建DubboInvoker,刷新本地服务列表。创建DubboInvoker的时候会创建ExchangeClient。更细节的流程可跳转 Dubbo源码解读-Consumer消费端服务列表刷新-CSDN博客

getClients(url)创建的ExchangeClient对象。

# DubboProtocol.java
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);
        // create rpc invoker.
        //getClients(url)是核心逻辑
        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }

        同样服务端在服务暴露时候,创建DubboExport,启动NettyServer时候创建ExchangeServer。更细节的流程可跳转至 Dubbo源码解析-Provider服务暴露Export源码解析-CSDN博客

ExchangeClient的作用

  • Consumer和Provider之间通讯的桥梁。消费端通过NettyClient,发送请求和接收服务端的响应。服务端通过NettyServer接收消费端的请求和发送返回结果。
  • 持有Handler的执行链。消费端接收到响应或者服务端接收到请求之后,按照Handler的执行链处理数据。

ExchangeClient的执行链

  • 消费端链路:
    • 请求链路:HeaderExchangeClient.request()->HeaderExchangeChannel.request()->NettyClient.send()->nettyChannel
    • 响应链路:NettyClientHandler->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->defalutFutrue.doReceive()
  •     服务端链路:
    • 请求链路:HeaderExchangeServer->*->NettyServer->NettyServerHandler
    • 响应链路:NettyServerHandler->MultiMessageHandler->HeartbeatHandler->AllChannelHandler->DecodeHandler->HeaderExchangeHandler->ExchangeHandlerAdapter.reply()->DubboExporter.getInvoker->Invoker执行[Filter1-》Filter2-〉invokerDelegete->AbstractProxyInvoker->wrapper->ServiceClass]

ExchangeClient初始化源码解析

        Exchange属于信息交换层,用于消费者发送请求,提供者接受请求,数据编码解码等,以request/response为中心。

概要流程

  1. DubboProtocol.refer创建DubboInvoker。构造函数执行getClient.
  2. 创建HeaderExchangeClient对象,持有NettyClient。NettyClient持有Handler执行链。
  3. 创建DubboInvoker对象,持有client对象,通过client对象发送请求和接受服务端响应。

详细流程

  1. consummer端的notify订阅事件(服务列表刷新)
  2. 最后会调用到DubboProtocol.Refer(),初始化client
    1. DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
  3. getClients(url)
  4. 获取连接个数配置:connections
  5. 一个ExchangeClient,就对应一个TCP长链接。
    1.  ExchangeClient[] clients = new ExchangeClient[connections];
  6. 创建长连接initClient(url);服务端列表中,每一个服务端对应一个长链接。
    1. url设置编解码codec
    2. url设置心跳时间。heartbeat
    3. 返回HeaderExchangeClient,TCP长链接,由这个client发起调用。这里建立了一个很长的链式调用的对象关系。
      1. 持有NettyClient,Netty客户端
        1. 构造函数:构造Handler调用
        2. doOpen():开启Netty客户端
        3. connect():连接到netty服务端
        4. 连接成功后的channel赋值一下
      2. 开启心跳线程

源码解析

1.DubboProtocol。根据配置的连接数,创建连接。

private ExchangeClient[] getClients(URL url) {
        // whether to share connection
        boolean service_share_connect = false;
        //获取connections配置参数,配置客户端跟服务端建立几个长连接
        int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
        // if not configured, connection is shared, otherwise, one connection for one service
        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;
    }

2.DubboProtocol.getSharedClient。

private ExchangeClient getSharedClient(URL url) {
        String key = url.getAddress();
        ReferenceCountExchangeClient client = referenceClientMap.get(key);
        if (client != null) {
            if (!client.isClosed()) {
                client.incrementAndGetCount();
                return client;
            } else {
                referenceClientMap.remove(key);
            }
        }

        locks.putIfAbsent(key, new Object());
        synchronized (locks.get(key)) {
            if (referenceClientMap.containsKey(key)) {
                return referenceClientMap.get(key);
            }

            //初始化长连接
            ExchangeClient exchangeClient = initClient(url);
            client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
            //建立缓存
            referenceClientMap.put(key, client);
            ghostClientMap.remove(key);
            locks.remove(key);
            return client;
        }
    }

3.DubboProtocol.initClient。创建Client并构造handler的执行链。

private ExchangeClient initClient(URL url) {

        // client type setting.
        String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));

        url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
        // enable heartbeat by default
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));

        // BIO is not allowed since it has severe performance issue.
        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(), " "));
        }

        ExchangeClient client;
        try {
            // connection should be lazy
            if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
                client = new LazyConnectExchangeClient(url, requestHandler);
            } else {
                //如果非懒加载  requestHandler是最后调用的handler
                client = Exchangers.connect(url, requestHandler);
            }
        } catch (RemotingException e) {
            throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
        }
        return client;
    }

4. 最后会调到HeaderExchanger。构建HeaderExchangeClient对象。持有NettyClient对象和Handler的执行链。

 @Override
    public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
        //这里建立了一个很长的链式调用的对象关系,是责任链的变种
        return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
    }

5.HeaderExchangeClient

public HeaderExchangeClient(Client client, boolean needHeartbeat) {
        if (client == null) {
            throw new IllegalArgumentException("client == null");
        }
        this.client = client;
        this.channel = new HeaderExchangeChannel(client);
        String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
        this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
        this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
        if (heartbeatTimeout < heartbeat * 2) {
            throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
        }
        //开启心跳线程
        if (needHeartbeat) {
            startHeartbeatTimer();
        }
    }

图解ExchangeClient的执行链

        消费端调用服务方法,会通过MockClusterInvoker->FailOverClusterInvoker->DubboInvoker.invoke->Client发送请求。具体怎么调用到DubboInvoker的,感兴趣的同学可以跳转到 Dubbo源码解读-Consumer调用流程解析-CSDN博客

        下图为client和handler发送和接受服务端响应的流程。其中client部分为发送请求流程,handler负责处理服务端响应的结果。

总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。

知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。

当然如果觉得文章写的不错,也可点击大赏按钮,深深的鼓励一下博主。哈哈!!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值