Dubbo源码分析:客户端基于dubbo协议的RPC并发调用
概述
对consumer而言,Dubbo协议对每个Service默认是基于Netty单一长连接和NIO异步通讯的,适合小数据大并发的服务调用。在consumer端,会对需要调用的每个服务都创建一个服务代理bean,即Reference,该服务代理bean在consumer就是一个跟使用Spring的@Service注解修饰的普通Service类bean一样注册到spring容器,也是单例的。
consumer端Service代理
Service在consumer端的服务Service代理的核心组件
- 客户端Service代理:每个远程Service接口,在consumer端对应dubbo-config包的一个ReferenceBean实例;
- 客户端Service调用器:ReferenceBean实例为一个服务代理refer,服务代理refer包含一个dubbo-rpc-dubbo包的DubboInvoker调用器invoker实例;
- 客户端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协议客户端单一长连接下并发调用的结果获取
还没有评论,来说两句吧...