pb消息自动注册插件
既然已经到了这里, 相信你已经学会了使用protobuf。
这个插件有两个部分:
其一就是自动把客户端和服务器之间通信用到的消息注册到对应的消息ID上,在codec中,通过id和消息类型的对应关系,可以很方便地进行消息的解析和编码。
其二的vt模块是使用了vtproto强化pb序列化的性能 这里通过代码生成自动注册到一个消息map中,用于序列化。
使用方法
-
首先,此插件依赖原版protoc。如果对pb在golang中的使用不熟悉,可以先学习一下protoc的使用方法。
-
安装插件 用于在编译proto文件的时候自动生成注册文件
go install github.com/murang/potato/pb/protoc-gen-autoregister@latest
或者用于一个消息id对应一个消息对的格式
go install github.com/murang/potato/pb/protoc-gen-autoregisterpair@latest
还需要安装vtproto插件 详情见vtproto
go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto@latest
- 编写proto文件
syntax = "proto3";
package your_pack; // 指定proto文件所属的包
option go_package = "/your_pack";
// 消息ID严格按照 c2s(客户端->服务器)/s2c(服务器->客户端)的前缀与消息名称通过下划线连接的方式命名
// 比如 c2s_Heartbeat 表示客户端->服务器的心跳消息
// 一条消息必然有对应的ID 所以必须在消息ID枚举中定义了消息 才能被自动注册
// 由于proto中名称不能重复 所以消息ID前缀小写 对应的消息名称前缀大写
// 具体格式参考如下消息
enum MsgId {
unknown = 0; // proto3默认枚举从0开始 保留此项用于占位 它不会被自动注册
c2s_Heartbeat = 1; // 心跳 客户端 -> 服务器
s2c_Heartbeat = 2; // 心跳 服务器 -> 客户端
}
message C2S_Heartbeat {
}
message S2C_Heartbeat {
int64 timestamp = 1; // 当前服务器时间戳
}
如果是一个消息id对应一个消息对的话 那么格式如下
enum MsgId {
Unknown = 0;
Hello = 100;
Notify = 101;
}
message C2S_Hello {
string name = 1;
}
message S2C_Hello {
string sayHi = 1;
}
// 主动通知消息 没有c2s
message S2C_Notify {
string content = 1;
}
- 编译proto文件 在proto文件所在的目录下(或者其他目录,请自行调整命令参数)
protoc --go_out=. \
--autoregister_out=. \
--go-vtproto_out=. \
--go-vtproto_opt=features=marshal+unmarshal+size \
*.proto
或者生成消息对注册
protoc --go_out=. \
--autoregisterpair_out=. \
--go-vtproto_out=. \
--go-vtproto_opt=features=marshal+unmarshal+size \
*.proto
- 检查生成的注册文件
your_prroto_autoregister.go
current_dir
├── your_prroto.proto
└── your_pack
├── your_prroto.pb.go
├── your_prroto_vtproto.pb.go
└── your_prroto_autoregister.go
// Code generated by protoc-gen-autoregister. DO NOT EDIT.
package your_pack
import (
"github.com/murang/potato/pb"
"github.com/murang/potato/pb/vt"
"reflect"
)
func init() {
pb.RegisterMsg(uint32(MsgId_c2s_Heartbeat), reflect.TypeOf(C2S_Heartbeat{}))
pb.RegisterMsg(uint32(MsgId_s2c_Heartbeat), reflect.TypeOf(S2C_Heartbeat{}))
vt.Register[*C2S_Heartbeat]()
vt.Register[*S2C_Heartbeat]()
}
- 在项目中使用vtproto的序列化 可以使用vt替代proto 如果消息没有没注册到vt,会fallback到proto的序列化
// 反序列化
msg := &C2S_Heartbeat{}
err = vt.Unmarshal(dataBytes, msg)
// 序列化
msg := &C2S_Heartbeat{
SayHi: "Hi",
}
dataBytes, err := vt.Marshal(msg)
如果是生成代码有编译错误,请检查是否按照上述格式编写proto文件,检查是否缺少ID对应消息体。