这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
基本概念
本地函数调用
func main() {
result := calculate(2, 3)
fmt.Println(result)
return
}
func calculate(x, y int) {
z := x * y
return z
}
本地函数调用流程:
- 将参数2和3压栈
- 通过函数指针找到calculate函数,进入函数,取出栈中的2和3,将其赋予x和y
- 计算x*y,将结果存在z
- 将z的值压栈,然后从calculate返回
- 从栈中取出z返回值,赋值给result
远程函数调用(RPC - Remote Procedure Calls)
- 函数映射
- 数据转换成字节流
- 网络传输
一次RPC的完整过程

调用方
发起调用 -> 打包参数 -> 发送 -> 等待响应 -> 接收 -> 解包返回值 -> 返回
被调用方
接收 -> 解包参数 -> 调用函数 -> 获得返回值 -> 打包返回值 -> 发送
RPC的好处
- 单一职责
- 可扩展性强
- 故障隔离
RPC带来的问题
- 服务宕机,对方应该如何处理
- 在调用过程中发生网络异常,如何保证消息的可达性
- 请求量突增导致服务无法及时处理,有哪些应对处理
分层设计
RPC框架由编解码层、协议层、网络通信层组成。
编解码层
负责将参数、返回值等数据编码为字节码、将字节码解码为数据。
生成代码
Client和Server依赖同一个IDL文件,生成不同语言的CodeGen,生成代码包含数据类型(结构体等)。
数据格式
- 语言特定的格式
依赖编程语言
- 文本格式
如JSON、XML、CSV等文本格式。
- 二进制编码
跨语言,高性能。常见的有Thrift的Thrift BinaryProtocol和Protobuf
TLV编码
- Tag: 标签,可以理解为类型
- Length: 长度
- Value: 值,Value也可以是个TLV结构
编解码方式的选择
- 兼容性
- 通用性
- 性能
协议层
概念
- 特殊结束符
用一个特殊字符作为每个协议的单元结束标示
- 变长协议
以室长加不定长的部分组成,定长部分需要描述不定长部分的长度
协议构造
将编码后的数据按一定的协议组织成数据包。
协议解析
从数据包中解析出数据。
网络通信层
网络层调用底层操作系统的Socket API发送和接收数据包。
Sockets API

网络库
- 提供易用API
封装底层Socket API
连接管理和事件分发
- 功能
协议支持: TCP、UDP、UDS等
优雅退出,异常处理
- 性能
应用层buffer减少copy
高性能定时器,对象池等
RPC框架的关键指标
稳定性
保障稳定性的策略
- 熔断:保护调用方,防止被调用的服务出现问题而影响到整个链路
- 限流:保护被调用方,防止大流量把服务压跨
- 超时控制:避免浪费资源在不可用节点上
提高请求成功率
- 负载均衡
- 重试
处理长尾请求
指明显高于平均响应时间,占比较小的主动。
- Backup Request: 发出请求后,如果预期时间内未收到响应,则再次发送主动。
实现策略的方式一般为注册中间件
易用性
开箱即用
周边工具
扩展性
- Middleware
- Option
- 编解码层
- 协议层
- 网络传输层
- 代码生成工具插件扩展
观测性
- Log、Metric、Tracing
- 内置观测性服务