回顾下:普通RPC框架需要做的:服务的注册,发现,暴露。服务注册包括:调用模块(负载均衡,容错,透明)。RPC协议包括(序列化,编码,传输),其实透明化的远程调用。RPC报文的格式:请求行,请求头和请求体。RPC协议相比HTTP要更加精简,传输的量要更少。今天主要说说传输这块,其实也是最复杂的,说这个意义,感觉只是使用dubbo,其实对开发者的意义不是很大,大家不需要了解底层的业务传输,但是如果要设计一个消息服务器,这可是一般的开发人员可以搞定的。必须有过一定的经验,参考过别人的一个远程实现。了解了这些对dubbo的性能调优和配置有更深层的认识,而不是dubbo,api文档里面的一些配置
底层看了,框架在变化,不变的永远是底层。底层通信服务用到与:android端、ios端的推送信息。与特殊的设备进行网络的通信传输。
(一)Dubbo基于Netty网络传输的实现
一个RPC协议实现由 通信模块、报文编解码模块、序列化模块组成,其中通信模块就是RPC网络传输的实现。其稳定性和性能就直接影响了RPC服务的稳定和性能。如何保证传输模块的稳定和性能呢?
如果想搞定传输模块的稳定和性能,必须要先了解RPC协议的组成,一直强调模块的拆分,模块内在进行模块的拆分,也就是一点一点的进行组成,像积木一样。
1.IO模型:
RPC框架必然一定:不是NIO就是AIO。
- BIO 同阻塞(一夫一妻制)
线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。一个连接一个线程。
- NIO 同步非阻塞(一夫多妻制)
线程发起IO请求,立即返回;内核在做好IO操作的准备之后,通过调用注册的回调函数通知线程做IO操作,线程开始阻塞,直到操作完成。一个请求一个线程。
- AIO 异步非阻塞
线程发起IO请求,立即返回;内存做好IO操作的准备之后,做IO操作,直到操作完成或者失败,通过调用注册的回调函数通知线程做IO操作完成或者失败。一个有效请求一个线程。
2.连接模型
网络传输模型:底层都是tcp连接。RPC一般使用长连接。
- 长连接
当客户端和服务端建立连接后,就连接了一个tcp连接。用了之后不会释放一直保存,一直进行通信。http是长连接。用完了才关闭,
http的keeplive 是多长时间主动的断开。
tcp的keeplive 是多久完成一次状态的检测,是保护连接。
长连接不适合传输比较大的字段。大文件把所有的都站住了,其他人过来没的玩了。
- 短连接
当连接完毕后,就释放掉连接。
大文件适合短连接。所有有IO线程。
3.线程分类
- IO线程
IO虽然是负责流的读写,其实耗时最多的就是业务上边的。编码和解码属于io线程。
- 服务端业务线程
tomcat 或者dubbo 设置线程池的线程,其实就是在设置的业务线程。
- 客户端调度线程
类实例化远程的类里面的方法,就是调度线程完成的。但是传输的过程都是通过的io线程。
- 客户端结果exchange线程
结果发送给调度线程。
- 保活心跳线程
保证连接一直是长连接。
- 重连线程
挂掉了重新连回来。
4.线程池模型
所有的线程都会涉及到线程池的概念。
- 固定数量线程池
- 缓存线程池
- 有限线程池
(二)长连接的创建与维护
Dubbo 长连接实现与配置
- 初始连接
引用服务|增加提供者==>获取连接===>是否获取共享连接==>创建连接客户端==>开启心跳检测状态检查定时任务===>开启连接状态检测
源码见:com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
- 心跳发送
在创建一个连接客户端同时也会创建一个心跳客户端,客户端默认基于60秒发送一次心跳来保持连接的存活,可通过 heartbeat 设置。
源码见:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient#startHeatbeatTimer
- 断线重连
每创建一个客户端连接都会启动一个定时任务每两秒中检测一次当前连接状态,如果断线则自动重连。
源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#initConnectStatusCheckCommand
- 连接销毁:
基于注册中心通知,服务端断开后销毁
源码见:com.alibaba.dubbo.remoting.transport.AbstractClient#close()
(三)通信线程协作流程
-
Dubbo 传输协作线程
1.客户端调度线程:用于发起远程方法调用的线程。
2.客户端结果Exchange线程:当远程方法返回response后由该线程填充至指定ResponseFuture,并叫醒等待的调度线程。
3.客户端IO线程:由传输框架实现,用于request 消息流发送、response 消息流读取与解码等操作。
4.服务端IO线程:由传输框架实现,用于request消息流读取与解码 和response编码与发送。
5.业务执行线程:服务端具体执行业务方法的线程 -
客户端线程协作流程
1.调度线程
- 调用远程方法
- 对request 进行协议编码
- 发送request 消息至IO线程
- 等待结果的获取
2.IO线程
- 读取response流
- response 解码
- 提交Exchange 任务
3.Exchange线程
- 填写response值 至 ResponseFuture
- 唤醒调度线程,通知其获取结果
- 服务端线程协作
1.IO线程:
- request 流读取
- request 解码
- 提交业务处理任务
2.业务线程:
- 业务方法执行
- response 解码
- 回写结果至channel
- 线程池
1.fixed:固定线程池,此线程池启动时即创建固定大小的线程数,不做任何伸缩。
2.cached:缓存线程池,此线程池可伸缩,线程空闲一分钟后回收,新请求重新创建线程。
3.Limited:有限线程池,此线程池一直增长,直到上限,增长后不收缩。
PS:上边的如果能搞明白,不是大神,也一只脚踏进了大神的行列。