dubbo源码学习2-dubbo协议源码分析
协议(Protocol)的概念
协议(Protocol)是指在计算机通信或网络交互中,双方事先约定好的规则和标准,用于规范数据如何打包、传输、接收和解释。
所以简单说就是规则,发送数据编码的规则,接收数据解码的规则
Dubbo中的协议
在Dubbo框架中,协议特指RPC(远程过程调用)的通信协议,主要包括:
Dubbo协议:Dubbo默认的自定义二进制协议,基于TCP长连接,二进制头部+序列化体,支持请求-响应模式
dubbo 协议数据包格式
dubbo协议的一个完整的数据包分为消息头和消息体
消息头的长度是 16 字节,在消息头中分为5段内容
magic:占用2个字节,第一个字节值默认是 -38,第二个字节值默认是 -69
flag: 占用一个字节,位于消息头第三位
status: 占用一个字节,位于消息头第4为
request id: 占用8个字节,位于消息头第5-11位,请求Id是一个 long 类型
data length: 占用4个字节,位于消息头最后4为,长度是一个int类型
以如下消息头为例,flag 是需要转为 8 位的二进制数据进行解析的
status 位值为0,是因为这是一个请求报文,status 仅在响应时有效,表示响应状态,20-OK,30-CLIENT_TIMEOUT,31-SERVER_TIMEOUT等
关于 flag 的分析
- 第0位:表示请求/响应标志(1-请求,0-响应)
- 第1位:是否为双向通信(1-是,0-否)
- 第2位:是否为事件消息(如心跳事件)
- 第3-7位:序列化类型编号
消息头分析完了,接下来我们根据源码来分析
dubbo是基于Netty通信的,在dubbo服务启动时,会初始化Netty的线程模型,设置了编解码器类型
InternalDecoder ( 解码器 ) 接收数据时用到
InternalEncoder ( 编码器 ) 发送数据时用到
NettyServerHandler Netty消息处理器
在接收到数据进行解码时,会通过
int saveReaderIndex = message.readerIndex();
记录当前读取位置,如果读取到的 msg 是Codec2.DecodeResult.NEED_MORE_INPUT 时,表示需要更多数据,则会通过重置读取数据的位置,防止出现半包问题
message.readerIndex(saveReaderIndex);
在 ExchangeCodec 中定义了消息头的长度为16,并且读取了消息头到 byte[] 中
在 ExchangeCodec 中还会执行以下步骤
- 检查magic值,检查高位是否等于 -38,低位是否等于 -69,在dubbo中,这两个值是固定的
- 如果可读数据长度小于16,则返回需要更多数据
- 从消息头中,从12位开始读取一个int 值为消息体长度,一个int 为4字节,刚好是消息头的最后4位
- 如果可读数据小于消息体长度加消息头长度,则返回需要更多数据
在 dubbo中从消息头读取消息长度,再读取对应长度的消息体是解决TCP 粘包问题的关键所在
读取消息头的第2位,为 flag,在这里 flag 多处做位运算
- 从 flag 计算出协议类型 proto
- 计算消息类型是 请求类型还是响应类型
- 如果是响应类型,判断是否是Response.OK
- 如果是请求类型,判断是否是双向包
- 从 proto 中可知是心跳类型还是普通消息类型
如果是请求类型时,使用 DecodeableRpcInvocation 解码实际的消息体
如果是响应类型时,使用 DecodeableRpcResult 解码实际的消息体
在消息的编解码处理完成之后,解码后的消息体会交给 NettyServerHandler 进行处理,NettyServerHandler则是dubbo接入服务的范围了
执行流程可以参考
dubbo源码学习1-服务提供方接入执行源码分析