Skip to content

Day 1 — 消息编解码(Codec)

学习目标

理解 RPC 协议设计的基本思路,实现序列化/反序列化层,为后续的 Server/Client 通信奠定基础。

核心概念

为什么需要自定义协议?

RPC 框架需要在网络上传输「调用哪个方法」和「参数是什么」两部分信息。我们设计了一个简单但完整的二层消息结构:

  • Header:携带元信息(调用方法名、序列号、错误信息)
  • Body:携带实际的参数/返回值数据

连接建立时,客户端先发送一个 JSON 编码的 Option,其中包含 MagicNumber(用于校验协议合法性)和 CodecType(协商使用哪种编解码方式)。

Codec 接口

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

通过接口抽象,我们可以轻松切换不同的编解码实现,目前提供三种:

  • GobCodec:使用 Go 原生的 encoding/gob,性能最优,但仅限 Go 语言间通信
  • JsonCodec:使用 encoding/json,通用性好,便于调试,但性能较低
  • ProtobufCodec:使用 google.golang.org/protobuf,跨语言生产首选,Schema 驱动

三种 Codec 对比

维度GobCodecJsonCodecProtobufCodec
编码格式Go 私有二进制文本 JSON二进制 Protobuf
跨语言仅 Go通用通用
可读性不可读人类可读不可读
性能~653K ops/s~727K ops/s~215K ops/s
消息体积
Schema无需无需.proto 文件
适用场景Go 内部通信调试/跨语言简易生产级跨语言

ProtobufCodec 采用长度前缀帧(Header 用 JSON,Body 用 proto.Marshal), 若 Body 类型未实现 proto.Message 接口则自动降级为 JSON。

bufio.Writer 的作用

Gob/Json 编码时使用 bufio.Writer 包裹底层连接,将多次小写入合并为一次系统调用,减少网络开销。 Protobuf 使用长度前缀帧,先序列化为完整 []byte 再一次性写入。

关键代码

文件说明
codec/codec.goHeader、Codec 接口、Option、Type 定义
codec/gob.goGobCodec 实现(流式编解码)
codec/json.goJsonCodec 实现(流式编解码)
codec/protobuf.goProtobufCodec 实现(长度前缀帧)
codec/codec_test.go单元测试 + Benchmark(三种 Codec)

运行

bash
# 运行示例
go run ./day1-codec/example/

# 运行测试
go test -v ./codec/

# 运行 benchmark
go test -bench=. ./codec/