基本概念
远程函数(客户端和服务端是不同的进程)调用:需要解决函数隐射,数据转换成字节流,网络传输的问题。
概念模型:
RPC 框架分层设计
编解码层
数据格式:
-
语言特定格式:许多语言都内嵌了自己的编码方法。
-
文本格式:如JSON,XML,CSV等文本格式
-
二进制编码:具有跨语言和高性能的优点。
- TLV 编码:Thrift 使用 TLV 编码
- Varint 编码:Protobuf 使用 Varint 编码
-
如果我们要选择编码模式,需要从几个方式进行考量呢?
- 兼容性:是否支持自动增加新的字段
- 通用型:是否支持跨平台,跨语言
- 性能:时间空间上的性能,比如数据大小,编码时常,描述字段大小等等。
-
生成代码和编解码层相互依赖,框架的编解码应当具备扩展任意编解码协议的能力
传输协议层
-
消息切分:以下介绍两种比较常见的
-
特殊结束符:比如用\r\n作为协议单位结束的标志。
-
变长协议:length+body
就是用前面不变长的字段内容来描述后面变长内容的大小。
-
-
协议构造
以 Thrift 的 THeader 协议为例:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 a b c d e f +----------------------------------------------------------------+ | 0| LENGTH | +----------------------------------------------------------------+ | 0| HEADER MAGIC | FLAGS | +----------------------------------------------------------------+ | SEQUENCE NUMBER | +----------------------------------------------------------------+ | 0| Header Size(/32) | ... +--------------------------------- Header is of variable size: (and starts at offset 14) +----------------------------------------------------------------+ | PROTOCOL ID (varint) | NUM TRANSFORMS (varint) | +----------------------------------------------------------------+ | TRANSFORM 0 ID (varint) | TRANSFORM 0 DATA ... +----------------------------------------------------------------+ | ... ... | +----------------------------------------------------------------+ | INFO 0 ID (varint) | INFO 0 DATA ... +----------------------------------------------------------------+ | ... ... | +----------------------------------------------------------------+ | | | PAYLOAD | | | +----------------------------------------------------------------+
- LENGTH 字段 32bits,包括数据包剩余部分的字节大小,不包含 LENGTH 自身长度
- HEADER MAGIC 字段16bits,值为:0x1000,用于标识 协议版本信息,协议解析的时候可以快速校验
- FLAGS 字段 16bits,为预留字段,暂未使用,默认值为 0x0000
- SEQUENCE NUMBER 字段 32bits,表示数据包的 seqId,可用于多路复用,最好确保单个连接内递增
- HEADER SIZE 字段 16bits,等于头部长度字节数/4,头部长度计算从第14个字节开始计算,一直到 PAYLOAD 前(备注:header 的最大长度为 64K)
- PROTOCOL ID 字段 uint8 编码,取值有: - ProtocolIDBinary = 0 - ProtocolIDCompact = 2
- NUM TRANSFORMS 字段 uint8 编码,表示 TRANSFORM 个数
- TRANSFORM ID 字段 uint8 编码,表示压缩方式 zlib or snappy
- INFO ID 字段 uint8 编码,具体取值参考下文,用于传递一些定制的 meta 信息
- PAYLOAD 消息内容
-
协议解析
网络通信层
使用网络库:是对底层Sockerts API的封装,并且支持多种协议,而且具有高性能
- 阻塞 IO 下,耗费一个线程去阻塞在 read(fd) 去等待用足够多的数据可读并返回。
- 非阻塞 IO 下,不停对所有 fds 轮询 read(fd) ,如果读取到 n <= 0 则下一个循环继续轮询。
第一种方式浪费线程(会占用内存和上下文切换开销),第二种方式浪费 CPU 做大量无效工作。而基于 IO 多路复用系统调用实现的 Poll 的意义在于将可读/可写状态通知和实际文件操作分开,并支持多个文件描述符通过一个系统调用监听以提升性能。 网络库的核心功能就是去同时监听大量的文件描述符的状态变化(通过操作系统调用),并对于不同状态变更,高效,安全地进行对应的文件操作。
字节内部 Kitex
中文教学文档: