这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记
远程函数调用(RPC)
RPC要解决的问题:
函数映射、数据转换成字节流、网络传输
RPC概念模型
User发起本地调用,将参数打包,交给RPC Runtime,传输给被调用端,RPC Runtime接收并将参数解包,调用真正的业务逻辑。
一次RPC的完整过程
不知道对方的方法和参数,需要IDL文件来约定,双方都依赖同一份IDL文件
调用端通过生成代码,用编码器编码成字节流,通过通信协议,传输给对端。对端解码后由上层处理。
RPC的好处
- 单一职责:有利于分工协作和运维开发,用户通过网关,可以看视频,看直播、购物、账号、广告,一个服务由一个团队负责
- 可扩展性强:资源使用率高,双十一压力大,可以对直播间、购物进行扩容
- 故障隔离:某一个服务故障,不会引起所有服务崩溃
RPC带来的问题
- 服务宕机
- 调用过程中网络异常、如何保障消息可达
- 请求量突增导致服务无法及时处理
分层设计
编解码层-》协议层-》网络传输层
用户自己编写业务逻辑代码->通过代码生成工具把IDL文件转换成不同语言对应的lib代码,里面封装了编解码逻辑->框架编解码->协议->通信
生成代码可以看成编解码的一部分,因为里面往往封装了编解码逻辑。
编解码层
-
生成代码
-
数据格式
语言的特定格式:编程语言内建的将内存对象转换为字节序列
文本格式:JSON、XML、CSV等文本格式,具有可读性
二进制编码:跨语言和高性能,常有Thrift的BinaryProtocol,Protobuf等
- 二进制编码
- 选型
兼容性:支持自动增加新的字段、而不影响老的服务,提高系统灵活度
通用性:支持跨平台、跨语言
性能:从空间和时间两个维度考虑,编码后数据大小和编码耗费时长
协议层
编解码后还要添加一些特定的元数据
- 概念
- 协议构造
- 协议解析
从内存中读取指定一部分数据,读取magic number知道是什么类型的协议,读取编解码方式解码获得消息体提交上层
网络通信层
- Sockets API
- 网络库
提供易用API:封装了底层Socket API,连接管理和事件分发
功能:协议支持:TCP\UDP和UDS等,优雅退出、异常处理等
性能:应用层buffer减少copy,高性能定时器、对象池等
RPC框架关键指标
稳定性-保障策略
熔断:保护调用方,防止被调用的服务出现问题而影响整个链路
A-B-C:C出现问题,B响应超时,A频繁请求B,导致B堆积大量请求而宕机
限流:保护被调用包,防止大流量把服务压垮
超时控制:避免浪费资源在不可用节点上,服务不可调用就快速返回
稳定性-请求成功率
负载均衡:多个服务分摊压力
重试:请求失败,重试几次,多次都失败才是失败
稳定性-长尾请求
长尾请求:明显高于平均响应时间的那部分占比比较小的请求
PCT99:响应的耗时从小到大排列,处于99%位置的请求就是PCT99请求,后面的1%就可以视为长尾请求
提高长尾请求成功率:
Backup Request(备份请求)
t3时间根据PCt99就应该返回,没返回就重发,时间是t4,否则是t1+t2
稳定性-注册中间件
注册中间件保证稳定性
易用性
扩展性
观测性
log 日志
metric 监控面板看服务QPS,延迟
tracing 链路跟踪 排查问题,层层请求每个阶段耗时,把整个链路串起来
内置观测服务:用户配置,环境变量,线程有多少、用到的中间件,可以通过http观测当前RPC框架的使用情况
高性能
企业实践
Kitex-整体架构
自研网络库
go原生库无法感知连接状态,在使用连接池时,池中存在失效连接,影响连接池的复用。
原生库存在goroutine暴涨的风险,一个连接一个goroutine的模式,由于连接利用率低下,存在大量goroutine占用调度开销,影响性能。
自研网络库Netpoll
解决无法感知连接状态问题,引入epoll主动监听机制,感知连接状态
解决goroutine暴涨风险:建立goroutine池,复用goroutine
提升性能:引入Nocopy Buffer,向上层提供NoCopy的调用接口,编解码层面零拷贝
扩展性设计
网络库优化
调度优化:
epoll_wait在调度上的控制
gopool重用goroutine降低同时运行协程数
LinkBuffer:
读写并行无锁,支持nocopy的流式读写
高校扩缩容
Nocopy Buffer池化,减少GC
Pool
引入内存池和对象池,减少GC开销
编解码优化
Codegen
预计算并预分配内存,减少内存操作次数,包括内存分配和拷贝
inline减少函数调用次数和避免不必要的反射操作
自研Go语言实现的Thrift IDL解析和代码生成器,支持完善的Thrift IDL语法和语义检查,并支持了插件机制-Thriftgo
JIT
使用JIT编译技术改善用户体验的同时带来更强的编解码性能,减轻用户维护生成代码的负担
基于JIT编译技术的高性能动态Thrift编解码器-Frugal
合并部署
微服务过微,传输和序列化开销越来越大,将亲和性强的服务实例尽可能调度到同一个物理机,远程RPC调用优化为本地IPC调用。
\