RPC原理与实践 | 青训营笔记

93 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第13天。

基本概念

本地函数调用

  • 值压栈、通过函数指针找到函数、取值计算结果、结果压栈、获取结果。

远程函数调用(remote procedure calls)

  • 相比本地函数调用,RPC调用需要解决的问题
    • 函数映射
    • 数据转换成字节流
    • 网络传输

RPC概念模型

  • 由五个模型组成:
    • User、Use-Stub、RPC-Runtime、Server-Stub、Server

一次RPC的完整过程

  • IDL(interface description language)文件
    • 通过一种中立方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信
  • 生成代码
    • 通过编译器工具把IDL文件转换成语言对应的静态库
  • 编码解码
    • 从内存中表示到字节序列的转换称为编码。反之为解码,也常常称为序列化和反序列化
  • 通信协议
    • 规范数据在网络中的传输内容和格式。除必须的请求/响应数据外,通常还会包含额外的元数据
  • 网络传输
    • 通常基于成熟的网络库走TCP/UDP传输

RPC的好处

  • 单一职责,有利于分工协作和运维开发
  • 可扩展性强,资源使用率更优
  • 故障隔离、服务的整体可靠性更高

RPC带来的问题

  • 服务宕机
  • 网络异常、消息可达性
  • 请求量突增导致服务无法及时处理

分层设计

image.png

编解码-数据格式

  • 语言特定格式:许多编程语言都内建了将内存对象编码为字节序列的支持。但缺点是将语言与数据绑死了
  • 文本格式:JSON、XML、CSV等文本格式,具有人类可读性。但是部分数据类型不能区分、精度也无法指定、没有产品模型约束、编解码采用反射机制、性能比较差。
  • 二进制编码:具备语言和高性能等优点,常见有Thrift的BinaryProtocol、Protobuf等。编码方式有很多
    • TLV编码:有额外内存开销
      • Tag标签,可以理解为类型
      • Length:长度
      • Value:值,Value也可以是个TLV结构

编解码层 - 选型

  • 兼容性:支持自动增加新的字段,而不影响老的服务,提高系统的灵活度
  • 通用性:支持跨平台、跨语言
  • 性能:
    • 时间:编码耗费时长
    • 空间:编码后数据大小

协议层 - 概念

  • 特殊结束符:一个特殊字符作为每个协议单元结束的标志
  • 变长协议:以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度

协议层 - 协议构造

  • 以 Thrift 的 THeader 协议为例

  • 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 消息内容

协议层 - 协议解析

image.png

网络通信层 - Sockets API

  • CLIENT : 打开Client------> connect --> session ---> close
  • SERVER : bind -> listen -> accept ----> session ---> close accept

网络通信层 - 网络库

  • 提供易用API
    • 封装底层Socket API
    • 连接管理和事件分发
  • 功能
    • 协议支持:tcp,udp和uds等
    • 优雅退出、异常处理等
  • 性能
    • 应用层buffer减少copy
    • 高性能定时器、对象池等

关键指标

稳定性

  • 保障策略
    • 熔断:保护调用方,防止被调用的服务出现问题而影响到整个链路
    • 限流:保护被调用方,防止大流量把服务压垮
    • 超时控制:避免浪费资源在不可用节点上
  • 请求成功率
    • 负载均衡
    • 重试
  • 长尾请求
    • 明显高于平均响应时间的少量请求
    • BackupRequest
  • 注册中间件
    • 超时、熔断等,尽可能保证服务的稳定性

易用性

  • 开箱即用:合理的默认参数选项、丰富的文档
  • 周边工具:生成代码工具、脚手架工具、支持protobuf和thrift、支持自定义的生成代码插件

扩展性

  • Middleware - Option - 编解码层 - 协议层 - 网络传输层 - 代码生成工具插件扩展

观测性

  • Log、Metric、Tracing - 内置观测性服务

高性能

  • 目标:高吞吐、低延迟 - 场景:单机多机、单连接多连接、单/多client 单/多 server 、不同大小的请求包 、 不同求情类型(ping pong streaming等) - 手段:连接池、多路复用、高性能编解码协议、高性能网络库

企业实践

  • 字节内部:Kitex

参考资料

juejin.cn/post/719632…