Trpc协议插件

38 阅读5分钟

codec 协议插件

概述

codec 层主要负责与 transport 层获取和提供原始二进制数据帧,之后进行处理数据包边界问题,提取出 trpc 协议的完整数据包 (包头和包体),并从包头解析出调用元数据和包体业务数据。

针对业务数据进行压缩/解压、序列化/反序列化,获取到符合接口请求协议的数据。

codec 包可以支持任意的第三方业务通信协议,只需要实现相关接口即可

核心接口

FramerBuilder

定义如何把二进制流封装为具有读取二进制帧 (物理链路传输,解决传输协议黏包粘包) 操作 APIFramer

通常一个连接会独享持有一个 FramerBuilder

数据帧包括帧头(含有魔数、空格等)、长度(数据载荷长度)和数据载荷。

所有 FramerBuilder 被注册在 transport.RegisterFramerBuilder(name string, fb codec.FramerBuilder) 方法内。

type FramerBuilder interface {
    // 传入的 Reader 可能来自客户端也可以来自服务端
	New(io.Reader) Framer
}

Framer

定义如何从 Reader 识别和读取 trpc 框架协议的二进制数据。

以下 trpc 标准框架协议

https://blog.hackerpie.com/images/posts/tRPC-Go/trpc-protocol.webp

// 由 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 优点:压缩率高,兼容性好。 缺点:速度慢于 SnappyCPU 消耗较高。
  • 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

定义业务数据(请求/响应)如何进行序列化,目前支持的序列化协议如下:

  • ProtobufSerializationTypePB):高效、跨语言、体积小,适合高性能 RPC 场景。缺点是可读性差,调试不便。
  • JSONSerializationTypeJSON):可读性好,易于调试和与前端交互,跨语言支持广泛。缺点是体积大、性能较低。
  • FlatBufferSerializationTypeFlatBuffer):零拷贝、高性能,适合需要极致性能的场景。缺点是使用复杂,生态不如 Protobuf 完善。
  • NoopSerializationTypeNoop):空序列化,直接传递字节流,适合特殊场景。缺点是无结构化信息。
  • XMLSerializationTypeXML/SerializationTypeTextXML):适合与老系统或部分 Web 服务对接。缺点是体积大、解析慢。
  • FormSerializationTypeForm)、FormDataSerializationTypeFormData)、GetSerializationTypeGet):主要用于 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 中。

如果需要自定义框架协议,则实现 CodecFrameBuilderFramer,并定义枚举值,将他们注册到 codec.Registertransport.RegisterFramerBuilder,自定义协议后,还需要指定 transport 层传输使用 trpcudp