这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天
基本概念
本地函数调用
func main() {
var a int = 2
var b int = 3
result := calculate(a, b)
fmt.Println(result)
}
func calculate(x, y int) int {
z := x * y
return z
}
远程函数调用
实现RPC调用需要具备的三个功能
- 函数映射
- 数据和字节流的转换
- 网络传输
PRC概念模型
PRC 的过程由 5 个模型组成:User、User-Stub、RPC-Runtime、Server-Stub、Server
RPC过程
- IDL -> 生成代码 -> 编解码 -> 通信协议 -> 网络传输 IDL:接口定义文件,如 protobuf 和 thrift 生成代码:protobuf 和 thrift 有对应的编译器,可以自动生成代码编解码:可读性强的有 json、xml 等,还有语言专属的编解码,例如 go 的 gob,二进制编解码有 protobuf 和 thrift
RPC的优点
- 单一职责,一个模块对应一个服务
- 可扩展性强,资源利用率高
- 故障隔离,可靠性强
使用RPC要注意的问题
- 服务宕机
- 如何保证消息可达
- 请求量徒增
RPC分层设计
编解码层
数据格式
语言特定的格式
- 许多编程语言都内建了将内存对象编码为字节序列的支持,例如 Java 有 java.io.Serializable
文本格式
- JSON、XML、CSV 等文本格式,具有可读性
二进制编码
-
具备跨语言和高性能等优点,常见有 Thrift 的 BinaryProtocal, Protobuf 等
-
TLV 编码:Thrift 使用 TLV 编码
-
Varint 编码:Protobuf 使用 Varint 编码
-
选型
兼容性
- 支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活性
通用性
- 支持跨平台、跨语言
性能
- 从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长
协议层
- 特殊符号结尾
- 不定长,例如 length、value、length、value 格式
网络通信层
- 在实际中,通常使用封装好的网络库来作为 RPC 框架的网络通信层
RPC关键指标
- 稳定性:熔断,限流,超时控制,负载均衡,重试(例如错误重试,或者长尾请求设置超时时间重试),这些策略通过中间件注册到 RPC 服务中。
- 易用性:开箱即用,周边工具(代码生成、脚手架工具)
- 扩展性:middleware,option,编解码层,协议层,网络传输,代码生成扩展工具都不是写死的。
- 观测性:log,metric,tracing(链式跟踪)、内置观测服务(即时报警)
- 高性能:目标(高吞吐,低延迟),手段(连接池,多路复用,高性能编解码协议 protobuf 或 thrift,高性能网络库(epoll))
高性能网络库
- epoll 主动监听机制,感知连接状态
- goroutine 池
- 内存池、对象池,减少 GC 开销
- 引入 NoCopy Buffer,实现编解码层面零拷贝
服务注册,服务发现
负载均衡策略:轮询,加权轮询,一致性哈希