Dubbo源码分析:客户端基于dubbo协议的RPC并发调用

落日映苍穹つ 2022-04-03 19:07 339阅读 0赞

概述

对consumer而言,Dubbo协议对每个Service默认是基于Netty单一长连接和NIO异步通讯的,适合小数据大并发的服务调用。在consumer端,会对需要调用的每个服务都创建一个服务代理bean,即Reference,该服务代理bean在consumer就是一个跟使用Spring的@Service注解修饰的普通Service类bean一样注册到spring容器,也是单例的。

consumer端Service代理

Service在consumer端的服务Service代理的核心组件

  1. 客户端Service代理:每个远程Service接口,在consumer端对应dubbo-config包的一个ReferenceBean实例;
  2. 客户端Service调用器:ReferenceBean实例为一个服务代理refer,服务代理refer包含一个dubbo-rpc-dubbo包的DubboInvoker调用器invoker实例;
  3. 客户端Service调用底层通讯: DubboInvoker实例invoker包含一个或多个dubbo-remoting包的ExchangerClient实例,默认为1个,通过connections参数指定。ExchangerClient的默认实现类为HeaderExchangeClient,HeaderExchangeClient使用Netty的Bootstrap作为远程通讯client,Bootstrap使用NioSocketChannelFactory作为channelFactory。再用HeaderExchangeChannel来封装Bootstrap实例client,而HeaderExchangeClient则使用HeaderExchangeChannel实例channel来发送请求,具体看下面的服务调用分析。
    在这里插入图片描述

DubboInvoker实例的创建过程

  • 在consumer端,每个Service接口的服务代理,底层都包含一个DubboInvoker实例。具体为在consumer应用启动,加载spring,创建dubbo的服务代理refer bean时,在refer对应proxy中会包含一个invoker:
  • 在ReferenceConfig中创建服务代理
    在这里插入图片描述
    createProxy(map)的实现:refprotocol为DubboProtocol的实例,refer方法生成DubboInvoker实例,最后封装到服务代理refer中:
    在这里插入图片描述
    在这里插入图片描述
    JavassistProxyProxy: createProxy默认使用javassist生成代理
    在这里插入图片描述
  • DubboProtocol的refer方法的实现:getClients获取底层通讯的client实例,默认为netty的client。根据connections参数,即Constants.CONNECTIONS_KEY,默认数量为1(即单一长连接),即所有调用这个服务代理refer的线程,使用同一个连接发送请求和接收响应。
    在这里插入图片描述
    DubboInvoker的doInvoker:对远程服务进行调用,使用client发送请求,根据是否是异步,返回ResponseFuture或者阻塞等到结果返回。
    在这里插入图片描述

ExchangerClient默认使用单一Netty长连接和NIO通讯

  • AbstractClient:ExchangerClient接口的抽象实现类,定义模板方法
    在这里插入图片描述
  • ExchangerClient接口基于Netty实现类,继承于AbstractClient
    使用NioSocketChannel作为客户端Channel,即使用NIO;
    bootstrap.setOption(“keepAlive”, true),使用TCP长连接
    在这里插入图片描述
    在这里插入图片描述

consumer的并发调用

使用过Spring MVC都知道,Controller,Service都是单例的,Controller接收客户端的请求,调用Service处理业务逻辑获取数据。请求是并发过来的,共享Controller,Service实例,而consumer的服务代理也是一个Service实例,dubbo协议默认又是基于netty单一长连接(也可以通过connections参数生成多个连接)和NIO异步通讯的,所以需要考虑如何来使得这些并发调用能够获取自身的数据,即请求都是通过一个管道发出去的,当服务端响应消息时,如何交给对应的客户端调用线程,而不会调用线程之间互相影响,数据混乱。

服务调用过程

consumer调用远程service的method ——> 服务代理refer ——> DubboInvoker调用器invoker的doInvoker ——> ExchangerClient发起服务调用并获取结果

ExchangerClient的实现:HeaderExchanger,数据包头定长的数据交换器

在这里插入图片描述
client:默认为dubbo-remoting包的NettyClient
channel:HeaderExchangeChannel

  • 底层HeaderExchangeClient调用:通过一条绑定的Netty长连接的channel发送请求Request,返回一个DefaultFuture(ResponseFuture的子类)
    在这里插入图片描述
  • 顶层DubboInvoker的doInvoke:使用HeaderExchangeClient发送请求,如果是异步请求,则通过new创建一个Result新实例(通过局部变量的方式包保证线程安全性)返回,一路往上返回,最终到最顶层的Controller。该Result包装了ResponseFuture,在最顶层Controller的调用线程,可以灵活控制,从该Result实例获取结果,如先做其他事情,最后在调用get方法阻塞获取结果(一般在Service中完成)。
    如果是同步请求,则阻塞等到结果再返回,此时最顶层Controller的调用线程,阻塞在这里了。
    在这里插入图片描述

并发调用的数据安全性

客户端基于单一长连接的并发调用结果,请参加我的另外一篇文章:Dubbo源码分析:Dubbo协议客户端单一长连接下并发调用的结果获取

发表评论

表情:
评论列表 (有 0 条评论,339人围观)

还没有评论,来说两句吧...

相关阅读