RPC| 青训营笔记

21 阅读5分钟

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

今日学习

  • RPC 相关的基本概念
  • RPC 框架的分层设计
  • 衡量 RPC 框架的一些核心指标

RPC 的基本概念

RPC的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server

  • 本地函数调用
  • 远程函数调用

一次RPC的完整过程

IDL文件:相比本地函数调用,远程调用的话我们不知道对方有哪些方法,以及参数长什么样,所以需要有一种方式来描述或者说声明我有哪些方法,方法的参数都是什么样子的,这样的话大家就能按照这个来调用,这个描述文件就是 IDL 文件。

生成代码:通过编译器工具把IDL文件转化为语言对应的静态库

编解码:从内存中表示到字节列的转化称为编码,反之为解码,也常做序列化和反序列化

通信协议:规范了数据在网络中的传输内容和格式,除必须响应的请求和响应数据外,通常还会包含额外的元数据

网络传输

通常基于成熟的网络库走TCP/UDP传输

image-20230210103524998

分层

image-20230210104450153

生成代码

image-20230210104518039

数据格式

  • 语言特定的格式

编程语言内置的把内存编码为字节序列的支持

  • 文本格式

Json,Xml,Csv文件等

  • 二进制编码

具备跨语言和高性能等优势等特点,常见的有Thrift的BinaryProtocol等

二进制编码

TLV编码

image-20230210104804463

TLV编码结构简单清晰,并且扩展性较好,但是由于增加了Type和Length两个冗余信息,有额外的内存开销,特别是在大部分字段都是基本类型的情况下有不小的空间浪费。

选型

  • 兼容性
  • 通用性

跨平台,跨语言

  • 性能

从时间和空间两个维度来考虑,也就是编码后数据大小和编码耗费时间长

协议构造

image-20230210110430924

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-20230210110811528

套接字编程中的客户端必须知道两个信息:服务器的 IP 地址,以及端口号。

socket函数创建一个套接字,bind 将一个套接字绑定到一个地址上。listen 监听进来的连接,backlog的含义有点复杂,这里先简单的描述:指定挂起的连接队列的长度,当客户端连接的时候,服务器可能正在处理其他逻辑而未调用accept接受连接,此时会导致这个连接被挂起,内核维护挂起的连接队列,backlog则指定这个队列的长度,accept函数从队列中取出连接请求并接收它,然后这个连接就从挂起队列移除。如果队列未满,客户端调用connect马上成功,如果满了可能会阻塞等待队列未满(实际上在Linux中测试并不是这样的结果,这个后面再专门来研究)。

Linux的backlog默认是128,通常情况下,我们也指定为128即可。 connect 客户端向服务器发起连接,accept 接收一个连接请求,如果没有连接则会一直阻塞直到有连接进来。得到客户端的fd之后,就可以调用read, write函数和客户端通讯,读写方式和其他I/O类似

read 从fd读数据,socket默认是阻塞模式的,如果对方没有写数据,read会一直阻塞着: write 写fd写数据,socket默认是阻塞模式的,如果对方没有写数据,write会一直阻塞着: socket 关闭套接字,当另一端socket关闭后,这一端读写的情况: 尝试去读会得到一个EOF,并返回0。

尝试去写会触发一个SIGPIPE信号,并返回-1和errno=EPIPE,SIGPIPE的默认行为是终止程序,所以通常我们应该忽略这个信号,避免程序终止。 如果这一端不去读写,我们可能没有办法知道对端的socket关闭了。

关键指标

稳定性

保障策略

  • 熔断
  • 限流
  • 超时控制

请求成功率

  • 负载均衡
  • 重试

长尾请求

  • BackupRequest

易用性

  • 开箱即用

    • 合理的默认参数选项、丰富的文档
  • 周边工具

    • 生成代码工具、脚手架工具

个人总结

这几节课都是企业开发比较实战的课程,学校用的老技术不大可能用的上,也不可能讲解,但是概念讲解的比较多,适合有实际开发经验的人,不适合没有接触过这些技术的人学习

引用

【后端专场 学习资料五】第五届字节跳动青训营 - 掘金 (juejin.cn)