一 基本概念
RPC: 远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
-
RPC底层需要的两大重要功能
- Serialization 序列化
- Transport 传输
-
相比本地函数调用,RPC调用需要解决的问题
- 函数映射
- 数据转换成字节流
- 网络传输
-
一次完整的PRC过程:IDL文件 - 编码 - 传输 - 解码
- IDL文件。IDL:接口描述语言。IDL文件定义统一的接口名称和参数,可以通过Thrift协议提供的编译器编译成不同的语言代码,实现跨语言调用。
- 生成代码。通过编译器工具把IDL文件转换成语言对应的静态库。
- 编码器。从内存中表示到字节序列的转换称为编码,反之为解码。也叫序列化和反序列化。
- 通信协议。规范数据在网络中的传输内容和格式,除了必须的请求/响应数据外,通常还包含额外的元数据。
- 网络传输。通常基于成熟的网络库走TCP/UDP传输。
-
RPC带来的问题
- 服务宕机如何感知?多次调用失败,熔断,降级
- 遇到网络异常应该如何应对?重试,长尾策略
- 请求量暴增怎么处理?限流
-
RPC的带来的好处
- 单一职责,有利于分工协作和运维开发
- 可扩展性强,资源利用率高,可以针对具体服务做扩容
- 故障隔离,服务的整体可靠性更高
二 分层设计
2.1 编解码层
2.1.1 类型
-
语言特定的格式
许多编程语言内建了将内存对象编码为字节序列的支持,如Java的java.io. Serializable
- 优势:方便,可以用很少的代码实现内存对象的保存和恢复
- 劣势:与特定语言深度绑定,其他语言很难读取,安全性和兼容性有问题
-
文本格式
JSON/XML/CSV等文本格式,具有人类可读性
- 优势:具有人类可读性
- 劣势:XML和CSV不能区分数字和字符串,JSON不能区分整数和浮点数,且不能指定精度。处理大量数据时,调试不便,性能差。
-
二进制编码
具备跨语言和高性能等优点,常见的有Thrift的BinaryProtocol和Protobuf等
2.1.2 选择
- 兼容性:支持自动增加新的字段,而不影响老的服务,以提高系统的灵活性
- 通用性:技术层面(支持跨平台、跨语言)。流行程度(使用人数多,第三方包丰富)
- 性能:空间(编码后大小)。时间(编码耗时)
2.2 协议层
2.3 网络通信层
-
提供易用的API
- 封装底层Socket API
- 连接管理和事件分发
-
功能
- 协议支持:tcp、udp、uds等
- 优雅退出、异常处理等
-
性能
- 应用层buffer减少copy
- 高性能定时器、对象池等
三 关键指标
3.1 稳定性
- 熔断:保护调用方,防止被调用的服务出问题而影响到整个链路。一个服务A调用服务B时,服务B的业务逻辑又调用了服务C,而这时服务C响应超时了,由于服务B依赖服务C,C超时直接导致B的业务逻辑一直等待,而这个时候服务A继续频繁地调用服务B,服务B就可能因为堆积大量的请求而导致服务宕机,从而导致服务雪崩。
- 限流:保护被调用方,防止大流量把服务压垮。当调用方发送请求时,服务端在执行业务逻辑前先检查限流逻辑,如果发现访问量过大并且超出了限流条件,就让服务端直接降级处理或者返回给调用方一个限流异常。
- 超时控制:避免浪费资源在不可用节点上。当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,避免浪费资源。
- 重试:重试有放大故障的风险,加大下游的负载,防止重试风暴,限制单点重试和链路重试
- 负载均衡:服务集群,均匀承担请求
- 长尾请求:请求响应耗时明显高于均值的部分占比较小的请求(后1%)。长尾请求总是存在,原因很多,如网络抖动,GC,系统调度等。可以设置t1时间(比超时时间短,通常是P99时间),当第一个请求的时间超过t1时,发出第二个请求,只要有一个请求返回结果就表示请求成功。这样整体耗时就是t2,表示第一个请求发出到第一个成功返回之间的时间,这种机制比超时后再重发请求整体耗时少。
3.2 易用性
- 开箱即用:合理的默认参数选项、丰富的文档
- 周边工具:生成代码工具、脚手架工具
3.3 扩展性
- Middleware
- Option
- 编解码层
- 协议层
- 网络传输层
- 代码生成工具插件扩展
一次请求发起首先会经过治理层面,治理相关的逻辑被封装在middleware中,这些middleware会被构造成一个有序的调用链逐个执行,比如服务发现,路由,负载均衡,超时控制等,middleware执行后就会进入remote模块,完成与远端的通信。
3.4 观测性
- Log、Metric、Tracing
- 暴露框架自身状态,如当前环境变量,配置,Client/Server初始化参数,缓存信息等
3.5 高性能
-
目标
- 高吞吐
- 低延迟
- 很多场景下低延迟更重要
-
手段
- 连接池
- 多路复用
- 高性能编码协议
- 高性能网络库
3.6 总结
- RPC框架通过中间件来注入各种服务治理策略,保障服务稳定性
- 通过提供合理的默认配置和方便的命令行工具可以提升框架的易用性
- 框架应当提供丰富的扩展点,如核心的传输层和协议层
- 观测性除了传统的Log、Metric、Tracing之外,内置状态暴露服务也很有必要
- 性能可以从多个层面去优化,例如选择高性能的编解码协议和网络库