主题
miniRPC 整体架构设计
1. 架构总览
miniRPC 采用分层架构设计,自底向上分为四个层次:
┌─────────────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ 用户定义的 Service 结构体与方法 │
├─────────────────────────────────────────────────────────┤
│ 框架层 (Framework) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐ │
│ │ Server │ │ Client │ │ XClient │ │ Middleware │ │
│ │ 服务注册 │ │ 同步/异步 │ │ 服务发现 │ │ 拦截器链 │ │
│ │ 请求分发 │ │ 连接管理 │ │ 负载均衡 │ │ 日志/恢复 │ │
│ └──────────┘ └──────────┘ └──────────┘ └────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 协议层 (Protocol) │
│ ┌──────────────────┐ ┌───────────────────────────┐ │
│ │ Option 握手协商 │ │ Codec 编解码接口 │ │
│ │ MagicNumber 校验 │ │ Gob │ Json │ Protobuf │ │
│ └──────────────────┘ └───────────────────────────┘ │
├─────────────────────────────────────────────────────────┤
│ 传输层 (Transport) │
│ TCP 长连接 │ HTTP CONNECT 协议切换 │
└─────────────────────────────────────────────────────────┘2. 核心组件关系
3. 请求生命周期
一次完整的 RPC 调用经历以下阶段:
4. 模块依赖图
┌──────────────┐
│ middleware/ │
│ 拦截器框架 │
└──────────────┘
│ (可选依赖)
┌───────────┐ ┌────┴─────┐ ┌──────────────┐
│ xclient/ │──│ Client │ │ Server │
│ 服务发现 │ │ 客户端 │ │ 服务端 │
│ 负载均衡 │ └────┬─────┘ └──────┬───────┘
└───────────┘ │ │
┌────┴───────────────┴────┐
│ service.go │
│ 反射注册 & 方法调用 │
└────────────┬─────────────┘
│
┌────────────┴─────────────┐
│ codec/ │
│ Gob │ Json │ Protobuf │
└──────────────────────────┘5. 关键设计决策
5.1 单连接多路复用
miniRPC 在单个 TCP 连接上支持多个并发 RPC 调用。通过 Seq(序列号)匹配请求和响应,避免为每次调用建立新连接。
优点:减少连接数,降低系统资源占用 代价:需要 pending map 管理状态,实现复杂度增加
5.2 Codec 可插拔
通过 Codec 接口和 NewCodecFuncMap 工厂注册表,编解码实现完全可插拔。新增编解码只需:
- 实现
Codec接口 - 在
init()中注册到NewCodecFuncMap
5.3 Option 握手
连接建立后,客户端先发送 JSON 编码的 Option。这一设计使得:
- 编解码方式可协商(不硬编码)
- 超时参数可按连接定制
- 扩展新字段向后兼容(JSON 忽略未知字段)
5.4 反射调用 vs 代码生成
miniRPC 使用运行时反射发现和调用方法,而非 gRPC 式的代码生成。
| 维度 | 反射 (miniRPC) | 代码生成 (gRPC) |
|---|---|---|
| 开发体验 | 无需 .proto + protoc | 需要工具链 |
| 类型安全 | 运行时检查 | 编译时检查 |
| 性能 | 反射有开销 | 零额外开销 |
| 跨语言 | 仅 Go | 多语言 |
miniRPC 选择反射是为了简化开发流程,适合学习和中小规模场景。
5.5 超时三层防护
连接超时 (ConnectTimeout)
└─ 防止 TCP 握手/协议协商过慢
调用超时 (context.Context)
└─ 防止单次 RPC 等待过长(客户端控制)
处理超时 (HandleTimeout)
└─ 防止服务端 handler 执行过久(服务端控制)三层超时相互独立,提供完整的端到端超时保护。
6. 扩展点
| 扩展点 | 接口/机制 | 示例 |
|---|---|---|
| 编解码 | Codec 接口 | 添加 MsgPack、Thrift 编码 |
| 服务发现 | Discovery 接口 | 接入 etcd、Consul、ZooKeeper |
| 负载均衡 | SelectMode 枚举 | 添加一致性哈希、加权轮询 |
| 中间件 | ServerInterceptor / ClientInterceptor | 添加鉴权、限流、链路追踪 |
| 传输协议 | Dial / Accept | 添加 Unix Socket、QUIC |
7. 线程模型
Server 端:
main goroutine → Accept 循环
└─ 每个连接: 1 个 serveCodec goroutine (串行读请求)
└─ 每个请求: 1 个 handleRequest goroutine (并行处理)
Client 端:
main goroutine → 发送请求 (send)
background goroutine → 接收响应 (receive)这种「读串行 + 处理并行 + 写加锁」的模型在保证正确性的同时最大化了并发度。