Skip to content

Day 1 技术设计文档 — 消息编解码

协议格式

连接建立后,通信流程如下:

客户端                              服务端
  |--- Option (JSON编码) ------------>|   握手:协商编解码方式
  |--- Header + Body (Codec编码) ---->|   请求 1
  |<-- Header + Body (Codec编码) -----|   响应 1
  |--- Header + Body (Codec编码) ---->|   请求 2
  |<-- Header + Body (Codec编码) -----|   响应 2
  ...

Header 字段定义

字段类型说明
ServiceMethodstring格式 Service.Method,标识调用目标
Sequint64客户端选择的序列号,用于匹配请求和响应
Errorstring服务端返回的错误信息,空字符串表示成功

Option 字段定义

字段类型说明
MagicNumberint固定值 0x3bef5c,用于校验协议合法性
CodecTypestring编解码类型:application/gobapplication/jsonapplication/protobuf
ConnectTimeoutDuration连接超时时间(Day 7 引入)
HandleTimeoutDuration处理超时时间(Day 7 引入)

Codec 接口设计

go
type Codec interface {
    io.Closer
    ReadHeader(*Header) error
    ReadBody(interface{}) error
    Write(*Header, interface{}) error
}

type NewCodecFunc func(io.ReadWriteCloser) Codec

采用工厂函数模式注册不同的 Codec 实现,通过 NewCodecFuncMap 映射 Type 到构造函数。

握手时序图

设计决策

为什么用 Header + Body 而非自描述格式?

自描述格式(如纯 JSON)虽然灵活,但每条消息都携带字段名等冗余信息。分离 Header 和 Body:

  • Header 结构固定、字段少,解析快速
  • Body 可以是任意类型,由具体的 Codec 负责序列化

Gob vs Json vs Protobuf

维度GobJsonProtobuf
编码格式Go 私有二进制文本二进制
性能 (ops/s)~653K~727K~215K
消息体积最小
跨语言仅 Go通用通用
可读性不可读人类可读不可读
Schema无需无需.proto 文件
依赖标准库标准库google.golang.org/protobuf

miniRPC 同时提供三种 Codec:

  • GobCodec(默认):Go 内部通信场景首选,流式编解码,内存分配最少
  • JsonCodec:调试和跨语言简易集成场景,人类可读
  • ProtobufCodec:生产级跨语言场景首选,消息体积最小

ProtobufCodec 的帧格式

ProtobufCodec 采用长度前缀帧(Length-Delimited Framing):

┌──────────────────┬──────────────────┬──────────────────┬──────────────────┐
│  Header 长度      │  Header 数据      │  Body 长度        │  Body 数据        │
│  (4 bytes, BE)   │  (JSON 编码)      │  (4 bytes, BE)   │  (proto.Marshal) │
└──────────────────┴──────────────────┴──────────────────┴──────────────────┘
  • Header 使用 JSON 编码(结构简单、字段少,JSON 足够高效)
  • Body 若实现 proto.Message 接口则使用 proto.Marshal,否则降级为 JSON
  • 4 字节大端序长度前缀,支持最大 4GB 单帧

三种 Codec 的实现差异

GobCodec / JsonCodecProtobufCodec
编码方式流式 Encoder/Decoder先序列化为 []byte 再写入
缓冲bufio.Writer 合并写入长度前缀自带帧边界
帧分隔编码器内部处理显式 4 字节长度前缀
空 Body 处理编码器处理写入 length=0 帧