深入浅出 RPC 框架 | 青训营笔记

88 阅读2分钟

基本概念

远程函数(客户端和服务端是不同的进程)调用:需要解决函数隐射,数据转换成字节流,网络传输的问题。

概念模型:

image-20220604162102960.png

RPC 框架分层设计

编解码层

数据格式:

  • 语言特定格式:许多语言都内嵌了自己的编码方法。

  • 文本格式:如JSON,XML,CSV等文本格式

  • 二进制编码:具有跨语言和高性能的优点。

    • TLV 编码:Thrift 使用 TLV 编码
    • Varint 编码:Protobuf 使用 Varint 编码
  • 如果我们要选择编码模式,需要从几个方式进行考量呢?

    • 兼容性:是否支持自动增加新的字段
    • 通用型:是否支持跨平台,跨语言
    • 性能:时间空间上的性能,比如数据大小,编码时常,描述字段大小等等。
  • 生成代码和编解码层相互依赖,框架的编解码应当具备扩展任意编解码协议的能力

img

传输协议层

  • 消息切分:以下介绍两种比较常见的

    • 特殊结束符:比如用\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 消息内容
  • 协议解析

    img

网络通信层

使用网络库:是对底层Sockerts API的封装,并且支持多种协议,而且具有高性能

img

  • 阻塞 IO 下,耗费一个线程去阻塞在 read(fd) 去等待用足够多的数据可读并返回。
  • 非阻塞 IO 下,不停对所有 fds 轮询 read(fd) ,如果读取到 n <= 0 则下一个循环继续轮询。

第一种方式浪费线程(会占用内存和上下文切换开销),第二种方式浪费 CPU 做大量无效工作。而基于 IO 多路复用系统调用实现的 Poll 的意义在于将可读/可写状态通知和实际文件操作分开,并支持多个文件描述符通过一个系统调用监听以提升性能。 网络库的核心功能就是去同时监听大量的文件描述符的状态变化(通过操作系统调用),并对于不同状态变更,高效,安全地进行对应的文件操作。

字节内部 Kitex

中文教学文档:

github.com/cloudwego/k…