这是我参与「第五届青训营 」伴学笔记创作活动的第 十四 天
RPC原理与实现
- RPC的基本概念
- PRC框架分层设计
- RPC框架的核心指标
- Kitex实践
基本概念
函数映射
在本地调用中,函数体是直接通过函数指针来指定的,我们调用哪个方法,编译器就自动帮我们调用对应函数指针。但远程调用某一个函数时,函数又是如何被指定的呢?
远程调用本质上也是一次网络请求,所以远程函数都应该有一个自己的ID,在做PRC时只要带上这个ID,再通过函数与ID的对照关系表,就可以确定函数的指定并执行。
参数传递
在本地调用时,我们只需要将参数压栈,然后让函数读取栈中的值。但是在远程调用时显然无法通过压栈来实现,如何传参呢?
这时候就需要调用方将参数转成字节流,作为请求的参数发送给被调用方,再由被调用方将字节流转化成自己能读取的格式。
网络传输
如何保证远程调用在网络上高效稳定地传输呢?
可以借鉴TCP协议的可靠传输,同时引入保证超时机制让远程调用被消费。
分层设计
编解码层
代码生成
通过代码生成工具把IDL文件转换成不同语言对应的lib代码,里面封装了编解码逻辑
数据格式
-
语言特定格式
- 语言内置字节序列器,java.io.Serializable
- 存在安全和兼容性问题
-
文本格式
- JSON、XML、CSV等,具有人类可读性
- 编码纯在歧义,XML和CSV不能区分数字和字符串,JSON不能区分整数和浮点数
- JSON存在需要采用反射机制进行序列化和反序列化,性能较差
-
二进制编码
- BinaryProtocol -> TLV编码、Protobuf -> Varint编码,具备跨语言和高性能等特点
选型
-
兼容性
- 支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活度
-
通用性
- 支持跨平台、跨语言
-
性能
- 从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时厂
协议层
消息切分
- 特殊结束符:以一个特殊字符作为每个协议单元的结束标志
- 变长协议:length+body,以定长的的length描述不定长的body
协议构造
- LENGTH: 数据包大小,不包含自身
- HEADER MAGIC: 标识版本信息,协议解析时候快速校验
- SEQUENCE NUMBER: 表示数据包的seqID,可用于多路复用,单连接内递增
- HEADER SIZE:头部长度,从第14个字节开始计算一直到PAYLOAD前
- PROTOCOL ID: 编解码方式,有Binary和Compact两种
- TRANSFORM ID: 压缩方式,如zlib和snappy
- INFO ID: 传递一些定制的meta信息
- PAYLOAD: 消息体
网络通信层
网络库
-
提供易用 API
- 封装底层Socket API
- 连接管理和事件分发
-
功能
- 协议支持:tcp、udp和uds等
- 优雅退出、异常处理等
-
性能
- 应用层buffer 减少 copy
- 高性能定时器、对象池等
核心指标
PRC框架衡量指标可以从以下五个方面着手:稳定性、易用性、扩展性、观测性、高性能
稳定性
- 熔断: 保护调用方,防止被调用的服务出现问题而影响到整个链路
- 限流: 保护被调用方,防止大流量把服务压垮
- 超时控制: 避免浪费资源在不可用节点上
- 负载均衡
- 重试: 防止重试风暴,限制单点重试和限制链路重试
- 长尾请求: 一般在是指明显高于均值占比较小的请求,业界常用标准P99标准
易用性
- 开箱即用:合理的默认参数选项、丰富文档
- 周边工具:代码生成工具、脚手架工具
拓展性
- 支持中间件拓展
- 编解码层拓展
- 代码生成工具插件拓展等等
观测性
- Log、Metric、Tracing三件套
- 内置观测性服务
- 暴露自身状态:环境变量、配置、Client/Server初始化参数、缓存信息
高性能
- 连接池化
- IO多路复用
- 高性能编解码协议
- 高性能网络库
Kitex实践
整体架构
- Kitex Core:主干逻辑,定义了框架的层次、接口,还有接口的默认实现
- Kitex Byted:对字节内部的拓展,继承了内部的二方库还有字节相关的非通用实现
- Kitex Tool:代码生成相关实现
自研网络库 Netpoll 优化
- 避免了原生库连接的无感知
- 避免了原生库存在goroutine暴涨的风险
性能优化
-
网络优化
-
调度优化
- epoll_wait 调度延迟优化
- 合理利用 unsafe.Pointer
- LinkBuffer 减少内存拷贝,从而减少 GC -> nocopy
- 引入内存池和对象池
-
-
编解码优化
- Codegen:预计算提前分配内存,inline,SIMD等
- JIT:无生产代码,将编译过程移到了程序的加载(或首次解析)阶段,可以一次性编译生成对应的 codec 并高效执行
课后
-
行业内各个流行的 RPC 框架的优劣对比
dubbo不支持跨语言,thrift和GRPC不支持服务治理并且支持的序列化格式单一