codec 协议插件
概述
codec 层主要负责与 transport 层获取和提供原始二进制数据帧,之后进行处理数据包边界问题,提取出 trpc 协议的完整数据包 (包头和包体),并从包头解析出调用元数据和包体业务数据。
针对业务数据进行压缩/解压、序列化/反序列化,获取到符合接口请求协议的数据。
codec 包可以支持任意的第三方业务通信协议,只需要实现相关接口即可
核心接口
FramerBuilder
定义如何把二进制流封装为具有读取二进制帧 (物理链路传输,解决传输协议黏包粘包) 操作 API 的 Framer。
通常一个连接会独享持有一个 FramerBuilder 。
数据帧包括帧头(含有魔数、空格等)、长度(数据载荷长度)和数据载荷。
所有 FramerBuilder 被注册在 transport.RegisterFramerBuilder(name string, fb codec.FramerBuilder) 方法内。
type FramerBuilder interface {
// 传入的 Reader 可能来自客户端也可以来自服务端
New(io.Reader) Framer
}
Framer
定义如何从 Reader 识别和读取 trpc 框架协议的二进制数据。
以下 trpc 标准框架协议

// 由 FramerBuilder.New 创建和返回
// 一般内部会维护着 io.Reader, ReadFrame()则从 Reader解析和获取框架协议
type Framer interface {
ReadFrame() ([]byte, error)
}
Codec
Codec 负责在业务数据 (请求/响应) 以及调用元数据 (服务名/接口等),和 trpc 框架协议数据流之间做相互转换。
所有 Codec 被注册在 codec.Register(name string, serverCodec Codec, clientCodec Codec) 方法内。
type Codec interface {
// 把msg调用元数据和业务数据封装为 trpc 框架协议数据帧
// client: Encode(msg, reqBody)(request-buffer, err)
// server: Encode(msg, rspBody)(response-buffer, err)
Encode(message Msg, body []byte) (buffer []byte, err error)
// 把 trpc 框架协议二进制数据拆解出调用元数据到msg以及业务数据到body返回
// server: Decode(msg, request-buffer)(reqBody, err)
// client: Decode(msg, response-buffer)(rspBody, err)
Decode(message Msg, buffer []byte) (body []byte, err error)
}
Compressor
负责把业务数据(请求/响应)进行数据压缩,目前支持的压缩算法如下:
Noop(不压缩) 优点:无性能损耗,速度最快。 缺点:不节省带宽,数据量大。Gzip优点:压缩率高,通用性好,广泛支持。 缺点:压缩/解压速度较慢,CPU消耗较高。Snappy优点:压缩/解压速度非常快,适合高吞吐场景。 缺点:压缩率低于Gzip。Zlib优点:压缩率高,兼容性好。 缺点:速度慢于Snappy,CPU消耗较高。StreamSnappy优点:适合流式数据,速度快。 缺点:压缩率一般,主要用于大数据流。BlockSnappy优点:适合分块处理,速度快。 缺点:压缩率一般,适合批量数据
所有的压缩算法都被注册在 codec.RegisterCompressor(compressType int, s Compressor) 方法内。
type Compressor interface {
// 把序列化后的协议传入要求压缩
Compress(in []byte) (out []byte, err error)
// 把压缩数据传入要求解压为反序列化前数据
Decompress(in []byte) (out []byte, err error)
}
Serializer
定义业务数据(请求/响应)如何进行序列化,目前支持的序列化协议如下:
Protobuf(SerializationTypePB):高效、跨语言、体积小,适合高性能RPC场景。缺点是可读性差,调试不便。JSON(SerializationTypeJSON):可读性好,易于调试和与前端交互,跨语言支持广泛。缺点是体积大、性能较低。FlatBuffer(SerializationTypeFlatBuffer):零拷贝、高性能,适合需要极致性能的场景。缺点是使用复杂,生态不如Protobuf完善。Noop(SerializationTypeNoop):空序列化,直接传递字节流,适合特殊场景。缺点是无结构化信息。XML(SerializationTypeXML/SerializationTypeTextXML):适合与老系统或部分 Web 服务对接。缺点是体积大、解析慢。Form(SerializationTypeForm)、FormData(SerializationTypeFormData)、Get(SerializationTypeGet):主要用于HTTP表单和GET请求参数序列化,适合Web场景。缺点是功能有限,结构化能力弱。
所有序列化算法都被注册在 codec.RegisterSerializer(serializationType int, s Serializer) 方法内。
type Serializer interface {
// 二进制数据要求序列化为指定结构体
Unmarshal(in []byte, body interface{}) error
// 原始请求/响应要求序列化为二进制数据
Marshal(body interface{}) (out []byte, err error)
}
对外操作 API
操作
API
// 指定调用option的时候, 对 protocol 指定时, 会选定调用使用的 Codec、FramerBuilder 和 transport
func WithProtocol(s string) Option
// 指定调用option的时候, 要求对下游数据传输使用指定的压缩算法
// 只是对请求指定压缩算法,响应的解压会根据下游使用的压缩算法进行解压
func WithCompressType(t int) Option
// 指定调用option的时候, 要求对下游数据传输使用指定的序列化算法
// 只是对请求指定序列化算法,响应的反序列化会根据下游使用的反序列化算法进行解压
func WithSerializationType(t int) Option
// 以下为 codec 包下 compress.go 文件源码,在使用WithCompressType时指定压缩枚举使用:
const (
CompressTypeNoop = iota
CompressTypeGzip
CompressTypeSnappy
CompressTypeZlib
CompressTypeStreamSnappy
CompressTypeBlockSnappy
)
// 以下为 codec 包下 serialization.go 文件源码,在使用WithSerializationType时指定压缩枚举使用:
const (
// 默认使用协议
SerializationTypePB = 0
SerializationTypeJSON = 2
// 枚举值1目前在腾讯内部有其他用途,保留内部使用
SerializationTypeFlatBuffer = 3
SerializationTypeNoop = 4
SerializationTypeXML = 5
SerializationTypeTextXML = 6
SerializationTypeUnsupported = 128
SerializationTypeForm = 129
SerializationTypeGet = 130
SerializationTypeFormData = 131
)
如何自定义框架协议?
如果需要自定义序列化,则实现 Serializer 并定义自定义的序列化枚举注册到 codec.RegisterSerializer 中。
如果需要自定义压缩,则实现 Compressor 并定义自定义的压缩算法枚举注册到 codec.RegisterCompressor 中。
如果需要自定义框架协议,则实现 Codec 、FrameBuilder 和 Framer,并定义枚举值,将他们注册到 codec.Register 和 transport.RegisterFramerBuilder,自定义协议后,还需要指定 transport 层传输使用 trpc 或 udp。