在实现RPC调用时,通讯双方传输的数据(消息)如何表达描述,设计时一般会考虑两个目标:
性能高
性能高包括两点:
- 将原始数据转换为消息数据的速度快
- 转换后的消息数据体积小
跨语言
RPC调用没有要求调用双方的编程语言必须相同,如果能做到跨语言调用是最好,这会方便产品开发中不同的功能服务以最合适的语言实现,然后使用RPC实现彼此调用。因此RPC调用中传输的消息数据应该尽量能让跟多的语言支持。
以Python为例:
1. 边界
在网络传输中,一方可能连续向另一方多次发送消息,收到数据的一方如何界定数据中包含几条消息,这便是消息边界问题。
考虑TCP传输控制协议,在一条TCP链接中可以多次发送数据,如果发送的数据过大,就会被TCP底层实现拆解为多个数据包依次发送;而如果发送的数据过小,又可能会将几条数据组装成一个数据包进行发送。
为了解决消息边界的问题,有两种较为常用的方法:分割符法和长度声明法。
分割符法
顾名思义,就是在每条消息的结尾放置一种特殊的分割符(一种常用的分割符是\r\n),表示已到达本条消息的末尾。
长度声明法
长度声明法是在消息的起始位置,用一个固定长度的整数值(通常为4字节)声明本消息的长度,接收者先读取出长度声明,再按照声明的长度读取出相应大小的数据即可。
例如,HTTP协议同时运用了这两种方法:
HTTP/1.0 200 OK\r\n
Server: Nginx\r\n
Content-Type: text/html; charset=utf-8\r\n
Content-Length: 5096\r\n
\r\n
# 此处为5096字节的数据
2. 内容
在具体消息内容的表现形式上,可以使用文本,也可以使用二进制。
文本
我们可以将数据转换为具备某种格式的字符串(如 JSON),将字符串作为消息内容发送。
比如一个RPC调用请求,方法名为divide,参数为200和100,我们可以用JSON字符串的形式来表示这个消息内容:
{
"name": "divide",
"params": {
"num1": 200,
"num2": 100
}
}
采用JSON这种方式,大多数编程语言都已有JSON转换的工具,实现起来相对便捷。但是我们能够看到,形成的消息数据不够精简,数据中有较为无意义的,如"、{、}、,、空白字符等,在网络传输中会造成浪费。
二进制
二进制方式就是将数据在内存中的一些列原始二进制位或字节直接在网络中传送,而无需转换为字符串再传送。
我们能够看到,采用原始二进制传递,可以省去中间转换的环节,而且数据量也会大大减少,效率更高。
如果使用二进制的方式来传递上面举例的RPC调用请求,该如何组织数据呢?这就需要实现RPC机制的设计人员来制订一个调用双方都遵守的协议规则,不同的设计人员可能有不同的想法。
3. 压缩
如果消息数据过大,为了减轻网络带宽的压力,可以考虑对消息数据进行压缩处理。
就如同我们平时对一些文件、视频等使用压缩软件进行压缩来减小大小一样,我们可以在构造好数据准备发送前,先用算法将数据进行压缩处理,然后通过网络发送到对端,对端收到数据后,先进行解压缩处理,然后得到原体积数据后再进行解析。
即使是比文本数据小的二进制数据,我们仍然可以进行压缩处理。
但是需要注意的是,压缩处理是一把双刃剑,虽然能减少数据量减轻带宽压力,但是同时额外增加了压缩和解压缩的过程,压缩和解压缩在处理的时候会有时间的消耗,会导致操作系统的负担加重。有时压缩的成本可能比减少数据量带来的收益还高,就得不偿失了。
所以是否采用压缩处理,要根据具体情况权衡利弊。