Apache Dubbo 的协议帧与编解码原理
前言
Apache Dubbo 是阿里巴巴开源的高性能可扩展分布式 RPC 框架,目前其在各大厂内部被广泛使用。RPC 框架客户端在发送消息前会对 POJO 的消息内容编码为二进制流,然后通过 TCP 通道发送到服务端,服务端接受到二进制流后需要解码为 POJO 对象,那么这中间过程真的是那么简单吗?其实不然,本 Chat 旨在揭开这神秘的面纱。
本 Chat 内容如下:
- 什么是 TCP 半包与粘包问题,如何解决?
- Apache Dubbo 自定义的协议帧是怎样的?
- Apache Dubbo 客户端发送消息时是如何对消息进行编码的?
- Apache Dubbo 服务端接受消息时是如何对消息进行解码的?如何处理半包、粘包问题的?
TCP 半包与粘包
大家都知道在客户端与服务端进行网络通信时候,客户端会通过 socket 把需要发送的内容进行序列化为二进制流后发送出去,然后二进制流通过网络流向服务器端,服务端接受到该请求后会解析该请求包,然后反序列化后对请求进行处理。这看似是一个很简单的过程,但是细细想来却会发现没有那么简单。如下图是客户端与服务端交互流程:
如上图首先在客户端发送数据时,实际是把数据写入到了 TCP 发送缓存里面的,如果发送的包的大小比 TCP 发送缓存的容量大,那么这个数据包就会被分成多个包,通过 socket 多次发送到服务端。而服务端获取数据是从接受缓存里面获取的,假设服务端第一次从接受缓存里面获取的数据是整个包的一部分,这时候就产生了半包现象,半包不是说只收到了全包的一半,是说收到了全包的一部分。
服务器读取到半包数据后,会对读取的二进制流进行解析,一般的会把二进制流反序列化为对象,这里服务器由于只读取了客户端序列化对象后的一部分,所以反序列会报错。
同理如果发送的数据包大小比 TCP 发送缓存容量小,并且假设 TCP 缓存可以存放多个包,那么客户端和服务端的一次通信就可能传递了多个包,这时候服务端从接受缓存就可能一下读取了多个包,这时候就出现了粘包现象,由于服务端从接受缓存获取的二进制流是多个对象转换来的,所以在后续的反序列化时候肯定也会出错。
常见的解决半包粘包的方法有下面 3 种。
比较常见方案是在应用层设计协议时候把协议包分为 header 和 body 两部分,header 里面记录 body 长度。当服务端从接受缓冲区读取数据后,如果发现数据大小小于包的长度则说明出现了半包,这时候就回退读取缓存的指针,等待下次读事件到来的时候再次测试。如果发现包长度大于了包长度则看如果长度是包大小整数倍则说明了出现了粘包,则循环读取多个包,否者就是出现了多个整包+半包,这时候读取整数个包然后回退半包的指针,可知 header 记录 body 长度。
还有一种方式是在多个包之间添加分隔符,使用分隔符来判断一个包的结束。
可知这种方式时候每个包大小可以不固定,当服务器端读取时候每次遇到分隔符就知道当前包结束了。
还有一种是包定长,就是每个包大小固定长度:
可知这种方式每个包的大小必须一致。
Apache Dubbo 自定义的协议帧
在 TCP 协议栈中,每层协议都有自己的协议报文格式,比如 TCP 协议是网络七层模型中的传输层,有 TCP 协议报文格式;在 TCP 上层是应用层,应用层协议常见的有 HTTP 协议等,Dubbo 协议作为建立在 TCP 协议之上的一种应用层协议,自然也有自己的协议包格式,Dubbo 协议也是参考 TCP 协议栈中的协议,协议内容由 header 和 body 两部分组成,其结构如下图:
其中协议头 header 格式如下图:
如上图图可知 header 总包含了 16 个字节的数据:
- 其中前两个字节为魔数,类似 Class 类文件里面的魔数,这里用来标识一个帧的开始,固定为 0xdabb,其中第一个字节固定为 0xda,第二个字节固定为 0xbb。
- 后面的一个字节是请求类型和序列化标记 id 的组合结果:requst flag|serializationId。
其中高四位标示请求类型,枚举值如下:
protected static final byte FLAG_REQUEST = (byte) 0x80;//1000protected static final byte FLAG_TWOWAY = (byte) 0x40;//0100protected static final byte FLAG_EVENT = (byte) 0x20;//0010
其中低四位标示序列化方式,枚举值如下:
DubboSerialization:0001
Hessian2Serialization:0010
JavaSerialization:0011
CompactedJavaSerialization :0100
FastJsonSerialization:0110
NativeJavaSerialization:0111
KryoSerialization:1000
FstSerialization: 1001
ProtostuffSerialization:1010
Apache Dubbo 的协议帧与编解码原理相关推荐
- IEEE802.16e 协议中LDPC编解码原理说明
信道编码 信号传输过程中,考虑到稳定性的需要,发送端可以对信息进行编码.目前在数据传输中,主要有三种误码控制的方法,即自动请求重发(ARQ).前向纠错(FEC)和混合纠错(HEC)方式. 在传 ...
- 蓝牙音频双剑客(二)--高质量音频分布协议(A2DP) SBC编解码算法
零. 概述 主要介绍下蓝牙协议栈(bluetooth stack)传统蓝牙音频协议之高质量音频分布协议(A2DP) SBC编解码算法 Codec Specific Infomation Element ...
- Base64编解码原理并用Java手工实现Base64编解码
Base64编解码原理 目前Base64已经成为网络上常见的传输8比特字节代码的编码方式之一.在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后进行签名或加密,之后再次Bas ...
- 一文读懂视频编解码原理
引子 谈到视频的编解码,我们会自然地想到H.264.HEVC/H.265这些权威的视频编解码标准:谈到标准,有人觉得这个是有专门机构去研究的,我们关心应用就好:即使有兴趣读了标准和相关技术,面对更多的 ...
- MPEG4编解码原理
### Date: 2017/3/19 ### Author: SoaringLee ###Content: MPEG4 ASP编解码原理 一.MPEG4的编码原理 1 编码原理MPEG-4编码器主要 ...
- 一文读懂视频编解码原理[通俗易懂]
一文读懂视频编解码原理[通俗易懂] 引子 谈到视频的编解码,我们会自然地想到H.264.HEVC/H.265这些权威的视频编解码标准:谈到标准,有人觉得这个是有专门机构去研究的,我们关心应用就好:即使 ...
- 哈夫曼编解码原理与实现【转载】
1. 哈夫曼编解码原理 霍夫曼编码(Huffman Coding)是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种. 霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编 ...
- yolov5的anchors及bbox的编解码原理
yolov5的anchors的编解码原理 yolov5的anchors及bbox的编解码原理 1.anchor的生成 1)base anchor的生成 2)base anchor的平移和复制 2.bb ...
- adpcm编解码原理及其代码实现
目录 1. 源代码 adpcm.h adpcm.c 2. adpcm编解码原理 1.adpcm编码原理 2.adpcm解码原理 注释说明 3. ADPCM数据存放形式 1. adpcm 数据块介绍 2 ...
最新文章
- 如何在CSDN上快速引用到网页内的局部位置?
- 闭锁CountDownLatch和栅栏CyclicBarrier之异同举例
- linux 命令 nohup 后台运行
- java stopself_如何正确停止前台服务?
- vmware nat模式网络不通_【EVENG入门】 03EVENG网络运行环境(桥接模式、NAT模式、Cloud连接虚拟设备)...
- Info.plist与Prefix.pch修改文件位置遇到的问题及解决方法
- Cannot forward after response has been committed问题解决及分析
- 3D呈现transform-style(CSS3)
- ERNIE 3.0 | 刷榜了!60多项任务取得最好效果
- 规则引擎 drools_Drools的入门初探
- 2款免费的图片压缩工具
- Mysql数据库自动备份
- 创建新的apple id_新的Apple Maps与Google Maps:哪个适合您?
- 怎样将UltraISO做的启动U盘还原成原来的样子
- Fresher的完美文本
- 图片加载框架Glide
- [转]计算机视觉入门
- VBScript终极破产版石头剪刀布游戏(VBS语言实现)
- 大数据,why python
- [监督学习]GDA 高斯判别分析