RPC框架中的消息传递和协议
RPC框架中的消息传递和协议
RPC是两个子系统之间进行的直接信息交互,它使用操作系统提供的套接字来作为消息的载体,以特定
的消息格式来定义消息的内容和边界;客户端和服务端都是通过文件描述符的读写API来访问操作系统内核
中的网络模块为当前的套接字分配的发送(send buffer)和接收(recv buffer)缓存
具体在这个过程中消息的内容是什么就需要我们深入RPC的协议进行学习了
协议设计的整体思想
对于一串消息流,我们必须能确定消息边界,提取出单条消息的字节流片段,然后对这个片段按照一
定的规则进行反序列化来生成相应的消息对象;消息的表示就是序列化后的消息字节流在直观上的表现
形式,文本的形式对人类比较友好,二进制的形式对计算机比较友好
每个消息都有其内部字段的结构,结构构成了消息内部的逻辑规则,程序要按照结构的规则来决定字段
序列化后的顺序
一、消息边界
RPC需要在一条TCP链接上进行多次消息传递,在连续的两条消息之间必须有明确的分隔符规则,以便
接受端可以将消息分割开来;基于TCP连接之上的单条消息如果过大,就会被网络协议栈拆分成多个数据包
进行传送,而如果消息过小,协议栈就有可能将多个消息合成一个数据包进行发送。对于接收端来说,它看
到的只是一串串的字节数组,如果没有明确的消息边界规则,接收端就不知道这一串字节数组包含了多少消息
比较常用的两种分割方式是特殊分隔符法和长度前缀法,分别如下图所示
特殊符分割法就是在每条消息的末尾追加一个特殊的分隔符,并且保证消息中间的数据不能包含特殊分
割符。比如最常见的分隔符是“ \r\n ”,当接受端遍历字节数组时发现了“\r\n”,就可以确定这个分隔符之前的
字节数组是一条完整的消息。在HTTP和Reids协议中就大量了使用“\r\n”分隔符,这种应用场景要求消息体的
内容是文本消息
这种方法的优点是消息的可读性比较强,可以直接看到消息的文本内容,但是不适合传递二进制消息,
因为二进制的字节数组中很容易出现“\r\n”分隔符的ASCII值,如果非要传递的话需要对二进制进行base64
编码转换成文本消息再进行传送
消息发送端在每条消息的开头再增加4字节长度的整数值,标记消息体的长度,这样消息的接受者就会
首先读取长度信息,然后再读取相应长度的字节数组就可以将一个完整的消息分离出来,常用于二进制消息
这种方法的优缺点与特殊分割符法正好相反。长度前缀法可读性很差,但是适用于二进制协议,但是
对于文本和内容都可以进行传递
HTTP协议是上述两种法的混合型协议,因为HTTP的消息头采用的是纯文本外加“\r\n”分隔符,而消息
体是通过消息头中的Content-length的值来决定长度的。HTTP协议可以传输文本协议,也可以传输二进制
数据,比如常见的音视频图像,所以被称为超文本传输协议
二、消息结构
每条消息都有着包含它的语义结构信息,有些消息协议的结构信息是显示的,还有些是隐式的。JSON
消息的结构就可以直接通过它的内容体现出来,可读性非常高,但是它有太多的冗余信息,比如每个字符
串都使用双引号界定边界,键值对之间必须有冒号进行分割,对象之间必须使用大括号进行分割;而且连
续的多条json消息即使结构完全一样,仅是value值不同,也需要发送同样的key字符串
消息的结构在同一条消息通道上是可以进行复用的,比如建立在链接的开始的RPC的客户端和
服务端之间先互相告知消息的结构,后续发送消息时只需要按照这个消息的模板发送消息就可以了,
接收端会自动将value值与相应位置的key关联起来,这样的模式可以节省比较多的流量
消息的隐式结构就是指那些结构信息由代码来约定的消息协议,在RPC交互的消息数据中只是纯粹的
二进制数据,由代码来确定对应位置的二进制是属于哪个字段的;单纯看消息的内容是无法知道具体字节
的含义的,它的消息结构是通过代码的结构顺序来确定的,这样的方式也可以节省传输的流量
三、消息压缩
如果消息的内容太大,就要考虑对消息进行压缩处理,这样可以减轻网络带宽,但是增大CPU的压力;
这两者之间一定要学会权衡。此外最好使用C语言实现的算法库,这样这样执行起来是比较快的,比较出名
的是Google的snappy算法