这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天
今日笔记内容: RPC原理与实现
基本概念
本地函数调用原理
- 参数压栈, 返回地址
- 根据函数指针将pc跳转到函数代码对应地址
- 取出参数
- 为本地变量分配栈空间
- 进行函数运算, 并将返回值压入栈中
- 回到调用函数
RPC要解决的问题
- 函数映射
- 数据转换为字节流
- 网络传输
Nelson在1984年在论文"Implementing Remote Procedure Calls"中提出了RPC的概念模型, 包括User, UserStub, RPC-Runtime, ServerStub, Server
![[Pasted image 20230209214248.png]] RPC的流程
- IDL文件, 以平台无关的方式定义接口
- 生成代码, 将IDL文件生成对应语言的源代码
- 编解码
- 通信协议
- 网络传输
RPC的好处
- 单一职责
- 可拓展性强
- 故障隔离
RPC带来的问题
- 服务宕机, 调用者如何处理
- 调用过程中发生网络异常, 保证消息的可达性
- 请求量突增导致服务无法及时处理
分层设计
- 用户代码
- IDL生成代码
- 数据编解码
- 传输协议
- 网络通信
编解码层的数据格式
- 语言特定的格式, 如java中的Serializable
- 文本格式, JSON, XML, CSV等
- 二进制编码, 如Thrift的BinaryProtocal, Protobuf等
- TLV 编码
- Tag: 标签, 标识类型
- Length: 长度
- Value: 值, 也可以嵌套TLV
- TLV 编码
编解码层的要求
- 兼容性: 协议新增字段不影响老服务
- 通用性: 跨平台, 跨语言使用
- 性能: 编码后的大小和编码时长
协议层: (最熟悉的TCP协议, 定义了两端通信的方式)
- 特殊结束符(表示包结束)
- 变长协议(length+payload)
协议构造 ![[Pasted image 20230209215700.png]]
- Length: 数据包大小
- Header Magic: 协议版本号, 魔数, 用于解析时快速校验
- Flags: 控制位
- Sequence Number: 标识数据包id, 可以用于多路复用, 单条链路中顺序递增
- Header Size: 变长头部时, 标识头部长度
- Protocol: 编解码方式, Binary, Compact
- Transform: 压缩方式, 如zlib, snappy
- Info: 额外信息
- Payload: 消息体
网络通信层: socket api
- 提供易用的API: 封装底层的SocketAPI, 连接管理和事件分发
- 功能: 多种协议支持. 连接状态管理, 优雅退出, 异常处理等
- 性能: 应用层buffer减少copy, 高性能定时器, 对象池等
关键指标
稳定性:
-
熔断: 保护调用方, 防止被调用服务出现问题导致整个链路不可用
-
限流: 保护被调用方, 防止大流量把服务压垮
-
超时控制: 浪费资源在不可用节点上
-
降级
-
调用成功率
- 负载均衡
- 重试(链路放大风险: 单点重试/链路重试上限
-
长尾请求: P99, 超出该时间后再次发送请求
-
易用性
-
可拓展性
-
可观测性
- Log
- Metric
- Tracing
- 内置观测服务
-
高性能
通过中间件实现稳定性策略
企业实践
自研网络库: Netpoll
原生网络库问题
-
原生库无法感知连接状态
-
存在goroutine暴涨的风险: 连接与goroutine是1:1的
-
引用epoll主动监听, 感知连接状态
-
goroutine池, 复用goroutine
-
nocopy buffer 提升性能, 编码层面0拷贝