主题
miniRPC 技术调研报告
1. RPC 框架全景
1.1 主流 RPC 框架对比
| 框架 | 语言 | IDL | 序列化 | 传输协议 | 服务发现 | 生态 |
|---|---|---|---|---|---|---|
| gRPC | 多语言 | Protobuf | Protobuf | HTTP/2 | 插件式 | 最完善 |
| Apache Thrift | 多语言 | Thrift IDL | 多种 | TCP/HTTP | 无内置 | 成熟 |
| Dubbo | Java/Go | 可选 | 多种 | TCP/HTTP | 内置 | 阿里系 |
| rpcx | Go | 无需 | 多种 | TCP/QUIC | 内置 | Go 生态 |
| net/rpc | Go | 无需 | Gob | TCP/HTTP | 无 | 标准库 |
| miniRPC | Go | 无需 | Gob/Json/PB | TCP/HTTP | 内置 | 学习项目 |
1.2 核心设计维度
一个 RPC 框架的核心设计涉及以下维度:
RPC 框架
├── 序列化协议 ─── 如何编码数据?
├── 传输协议 ───── 如何传输数据?
├── 服务描述 ───── 如何定义接口?
├── 服务发现 ───── 如何找到服务?
├── 负载均衡 ───── 如何分配请求?
├── 容错机制 ───── 如何处理故障?
└── 可观测性 ───── 如何监控运行?2. 序列化协议调研
2.1 候选方案对比
| 协议 | 格式 | 速度 | 体积 | 跨语言 | Schema | 可读性 |
|---|---|---|---|---|---|---|
| JSON | 文本 | 慢 | 大 | 通用 | 无需 | 高 |
| Protobuf | 二进制 | 快 | 小 | 通用 | .proto | 低 |
| Gob | 二进制 | 快 | 中 | 仅 Go | 无需 | 低 |
| MessagePack | 二进制 | 快 | 小 | 通用 | 无需 | 低 |
| FlatBuffers | 二进制 | 最快 | 最小 | 通用 | .fbs | 低 |
| Thrift Binary | 二进制 | 快 | 小 | 通用 | .thrift | 低 |
2.2 miniRPC Benchmark 实测数据
在 AMD EPYC 7K83 上的实测结果:
| Codec | 吞吐 (ops/s) | 延迟 (ns/op) | 内存 (B/op) | 分配次数 |
|---|---|---|---|---|
| Gob | ~653K | 1907 | 16 | 1 |
| Json | ~727K | 1747 | 16 | 1 |
| Protobuf | ~215K | 5107 | 96 | 4 |
注:Protobuf 的延迟较高是因为使用了长度前缀帧 + JSON 编码 Header 的混合方案。 在纯 Body 编解码上,Protobuf 的序列化效率实际上优于 JSON。 对于大体积消息和跨语言场景,Protobuf 的优势会更加明显。
2.3 miniRPC 的选择
miniRPC 提供三种编解码,适用于不同场景:
| Codec | 适用场景 |
|---|---|
| GobCodec | Go-to-Go 通信,开发调试阶段,追求极致性能 |
| JsonCodec | 跨语言互通,需要可读性,调试排查 |
| ProtobufCodec | 跨语言生产环境,消息体积敏感,有 Schema 管理需求 |
3. 传输协议调研
3.1 候选方案对比
| 协议 | 特性 | 优点 | 缺点 |
|---|---|---|---|
| TCP | 长连接、全双工 | 低延迟、高吞吐 | 无法穿透代理 |
| HTTP/1.1 | 请求-响应 | 通用、可穿透代理 | 队头阻塞、头部开销 |
| HTTP/2 | 多路复用、头部压缩 | gRPC 首选 | 实现复杂 |
| QUIC | UDP 上的可靠传输 | 0-RTT、无队头阻塞 | 新协议、NAT 穿越 |
| Unix Socket | 本地进程间 | 零网络开销 | 仅限本机 |
3.2 miniRPC 的选择
miniRPC 采用 TCP 长连接 作为主传输,同时支持 HTTP CONNECT 协议切换:
- TCP 直连:最低延迟,适合内网微服务
- HTTP CONNECT:复用 HTTP 基础设施(反向代理、LB),然后切换到 TCP 长连接
选择理由:TCP 实现简单且性能最优;HTTP CONNECT 提供部署灵活性。
4. 服务发现调研
4.1 候选方案对比
| 方案 | 一致性 | 可用性 | 复杂度 | 代表 |
|---|---|---|---|---|
| 硬编码 | - | - | 最低 | 配置文件 |
| DNS | 最终一致 | 高 | 低 | DNS SRV |
| ZooKeeper | 强一致 (CP) | 中 | 高 | Dubbo 默认 |
| etcd | 强一致 (CP) | 中 | 中 | Kubernetes |
| Consul | CP + AP 可选 | 高 | 中 | HashiCorp |
| Eureka | 最终一致 (AP) | 最高 | 低 | Spring Cloud |
| Nacos | CP + AP 可选 | 高 | 中 | 阿里巴巴 |
4.2 miniRPC 的选择
miniRPC 通过 Discovery 接口抽象服务发现,当前内置 MultiServerDiscovery(静态列表)。
接口设计使得接入任何注册中心只需实现 4 个方法:
go
type Discovery interface {
Refresh() error
Update(servers []string) error
Get(mode SelectMode) (string, error)
GetAll() ([]string, error)
}扩展示例:
go
type EtcdDiscovery struct {
client *clientv3.Client
servers []string
// ...
}
func (d *EtcdDiscovery) Refresh() error {
// Watch etcd key prefix, update servers
}5. 负载均衡调研
5.1 策略对比
| 策略 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 随机 | rand 选择 | 实现简单 | 短期不均匀 | 无状态、节点同质 |
| 轮询 | 顺序分配 | 绝对均匀 | 不感知负载 | 请求耗时均匀 |
| 加权轮询 | 按权重分配 | 适应异构节点 | 需配置权重 | 节点性能不同 |
| 最少连接 | 选连接数最少 | 自适应 | 需维护计数 | 长连接场景 |
| 一致性哈希 | 哈希映射 | 缓存亲和 | 实现复杂 | 有状态服务 |
| P2C | 随机两选一 | 低开销、自适应 | 略有偏差 | 大规模集群 |
5.2 miniRPC 的选择
miniRPC 内置 Random 和 RoundRobin,通过 SelectMode 枚举扩展:
go
const (
RandomSelect SelectMode = iota
RoundRobinSelect
// 可扩展:
// WeightedRoundRobinSelect
// ConsistentHashSelect
// LeastConnectionSelect
)6. 容错机制调研
6.1 常见策略
| 策略 | 说明 | miniRPC 实现 |
|---|---|---|
| 超时控制 | 限制等待时间 | ✅ 三层超时 |
| 重试 | 失败后重试 | ❌ 可通过拦截器扩展 |
| 熔断 | 错误率过高时快速失败 | ❌ 可通过拦截器扩展 |
| 限流 | 控制请求速率 | ❌ 可通过拦截器扩展 |
| 降级 | 返回兜底结果 | ❌ 应用层实现 |
| Failover | 切换到其他节点 | ⚠️ XClient 部分支持 |
6.2 miniRPC 超时三层防护
┌─────────────────────────────────────────────┐
│ ConnectTimeout (客户端) │
│ └─ net.DialTimeout: TCP 握手 + 协议协商 │
├─────────────────────────────────────────────┤
│ Context Timeout (客户端) │
│ └─ select { ctx.Done / call.Done } │
├─────────────────────────────────────────────┤
│ HandleTimeout (服务端) │
│ └─ select { time.After / called } │
└─────────────────────────────────────────────┘7. 可观测性调研
7.1 三大支柱
| 维度 | 说明 | miniRPC 实现 |
|---|---|---|
| 日志 (Logging) | 请求级别的文本记录 | ✅ AccessLog 拦截器 |
| 指标 (Metrics) | 聚合的数值统计 | ⚠️ methodType.numCalls 基础计数 |
| 链路追踪 (Tracing) | 跨服务调用链 | ❌ 可通过 context + 拦截器扩展 |
7.2 调试页面
miniRPC 提供 /debug/minirpc 页面,展示:
- 已注册服务列表
- 每个方法的参数类型
- 累计调用次数
8. miniRPC 与 gRPC 的差异总结
| 维度 | miniRPC | gRPC |
|---|---|---|
| 定位 | 学习项目 | 生产级框架 |
| IDL | 无需(反射) | 必须(.proto) |
| 序列化 | Gob/Json/Protobuf 可选 | 仅 Protobuf |
| 传输 | TCP + HTTP CONNECT | HTTP/2 |
| 流式调用 | 不支持 | Unary + Server/Client/Bidi Stream |
| 代码生成 | 无 | protoc-gen-go |
| 拦截器 | 基础支持 | 完善(Unary + Stream) |
| 服务发现 | 内置简单实现 | 需要 resolver 插件 |
| 连接管理 | 单连接 + pending map | HTTP/2 多路复用 |
| 健康检查 | 无 | 内置 Health Check |
| 生产就绪 | 否 | 是 |