RPC
1. 基础概念
本地函数调用
远程函数调用:付款
- 函数映射:哪个函数,本地函数指针即可,远程则需要确定函数
- 数据转换成字节流:参数,本地共享内存即可,远程需要传输
- 网络传输
RPC的5个模块:
- User:调用和返回结果
- User-Stub:参数打包和解压
- RPC-Runtime:发送、接收参数包
- Server-Stub:参数打包和解压
- Server:执行函数
基本概念:
- IDL:interface description language,记录有哪些方法,各自的参数是什么
- 生成代码:通过IDL文件,生成静态库
- 编解码:内存数据➡️字节序列
- 通信规范:数据传输时的内容格式,即通信协议
- 网络传输:TCP等
RPC的优点:
- 单一指责,不同的服务分离,开发语言都可以不同,分工和运维都独立
- 可扩展性强,可以独立扩充资源,针对特定服务进行扩容即可。底层服务可以复用。
- 故障隔离,某个服务故障不引起整体崩溃
RPC的缺点:
- 被调用服务宕机
- 调用过程网络异常
- 请求量突增
2. 分层设计
1. Apache Thrift框架:
code层,generated code层, TProtocal层,TTransport层,Network IO层
2. 编解码层:
-
依赖同一份IDL文件,生成不同语言的CodeGen
-
数据格式:语言特定的格式(如java的Serializable)、文本格式(如json)、二进制编码(如BinaryProtocol、Protobuf)
-
TLV编码:
- Tag:标签,即类型
- Length:长度
- Value:值,value也可以是TLV结构
-
选型:
- 兼容性:可新增字段
- 通用性:跨平台、跨语言,流行程度
- 性能:空间,编码后尽量小;时间,编解码耗费时间短
3. 协议层
概念:
- 特殊结束符,比如\r\n
- 变长协议,有一个length表示消息的长度
协议的构造:
length、header magic、sequence number、header size、protocol id、transform id、info id、 payload
协议解析:magicNumber➡️PayloadCode➡️Payload
4. 网络通信层
socket api
网络库:封装底层api,封装多种协议,优雅退出、线程池等性能
3. 关键指标
1. 稳定性
-
降级:以上都是降级的一种实现措施
- 熔断策略:避免雪崩
- 限流:防止大流量
- 超时控制:避免浪费资源在不可用节点
- 请求成功率:负载均衡、重试
- 备份请求:减少常规请求的延迟
2. 易用性
- 开箱即用:合理的默认参数选项,丰富的文档
- 周边工具:生成代码工具、脚手架工具
3. 扩展性
MiddleWare、option、编解码层、协议层、网络传输层、代码生成工具扩展
4. 观测性
Log、Metric、Tracing
内置观测性服务
5. 性能
-
场景
- 单机多机
- 单连接多连接
- 单/多client 单/多server
- 请求包的大小
- 请求类型,比如pingpong,streaming
-
目标
- 高吞吐
- 低延迟
-
手段
- 连接池
- 多路复用
- 高性能编解码协议
- 高性能网络库
4. 企业实践
1. 整体架构
kitex core:核心组件
kitex tool: 代码生成工具
kitex byted: 基础设施集成
2. 自研网络库NetPool
背景:
- go原生库无法感知连接状态,存在失效连接,影响连接池复用
- go原生库存在goroutin暴涨风险,占用调度开销,影响性能
解决方案:
- 引入epoll主动监听机制,感知连接状态
- 建立goroutine池
- 引入Nocopy Buffer,向上提供NoCopy调用接口,编解码层实现零拷贝
3. 扩展性设计
接口层:ping - pong、streaming、oneway
编解码层:thrift、 protobuf
应用层:TTHeader、HTTP2
传输层:tcp、udp、rdma
4. 性能优化
网络库优化
- 调度优化
- LinkBuffer
- Pool
编解码优化
- codeGen
- JIT
5. 合并部署
问题:微服务过小,传输、序列化开销过大
解决思路:将亲和力强的服务实例调度到一个物理机,rpc优化为本地ipc