Protocol Buffer Go (proto3) - macos 搭建 protocol buffer 环境 + Encoding 实验
macos 搭建 protocol buffer 环境
-
安装 protoc
brew install protobuf
-
为 protoc 安装 go 插件
$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest \ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
这些插件不能在同一条命令里执行安装,因为同一条
go install
命令安装的插件,必须是在同一个module
下的,某则会有错误提示:All packages must be provided by the same module
.
Encoding实验
参考文档: Encoding
- protocol buffer wire types 列表
Type Meaning Used For 0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum 1 64-bit fixed64, sfixed64, double 2 Length-delimited string, bytes, embedded messages, packed repeated fields 3 Start group groups (deprecated) 4 End group groups (deprecated) 5 32-bit fixed32, sfixed32, float - 定义 proto 如下
使用 protoc 将syntax = "proto3"; message StringMessage { string value = 1; int32 count = 2; int64 num = 3; string home = 4; } message StringMessage2 { string val = 1; }
protofile.proto
转换成 golang 结构
生成protoc \ --go_out=./gen/ \ --go_opt paths=source_relative \ protofile.proto
protofile.pb.go
如下:type StringMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"` Num int64 `protobuf:"varint,3,opt,name=num,proto3" json:"num,omitempty"` Home string `protobuf:"bytes,4,opt,name=home,proto3" json:"home,omitempty"` } type StringMessage2 struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Val string `protobuf:"bytes,1,opt,name=val,proto3" json:"val,omitempty"` }
- main函数
package main import ( "fmt" pb "path_to_generated_pb_go" "google.golang.org/protobuf/proto" ) func main() { msg := &pb.StringMessage{Value: "abc", Count: 127, Num: 128, Home: "China"} fmt.Println("msg:") fmt.Println(msg) out, _ := proto.Marshal(msg) fmt.Println(out) msg2 := &pb.StringMessage2{} proto.Unmarshal(out, msg2) fmt.Println("\nmsg2:") fmt.Println(msg2) out2, _ := proto.Marshal(msg2) fmt.Println(out2) }
- 执行结果
msg: value:"abc" count:127 num:128 home:"China" [10 3 97 98 99 16 127 24 128 1 34 5 67 104 105 110 97] msg2: val:"abc" 2:127 3:128 4:"China" [10 3 97 98 99 16 127 24 128 1 34 5 67 104 105 110 97]
- 序列化结果分析
[10 3 97 98 99]
这一段10
由 StringMessage.Value 的 fieldNumber 1 和 wireType 2 (string) 构成:
(1<<3|2) = 10
3
表示接下来3个字节构成字符串97 98 99
是abc
的 ASCII 编码
[16 127]
这一段16
由 StringMessage.Count 的 fieldNumber 2 和 wireType 0 (int32) 构成:
(2<<3|0) = 16
127
用一个字节足以表示127
[24 128 1]
这一段24
由 StringMessage.Num 的 filedNumber 3 和 wireType 0 (int64) 构成:
(3<<3|0) = 24
128 1
二进制为1000 0000 0000 0001
,根据 protobuf 的规则,首个1
表示后续字节依然要跟本字节一起表示数字;第二个字节的首个 bit 为0
,表明后续自己跟本字段无关。且 protobuf 将数字按照bigEndian
存储,所以解码后的数字为000 0001 000 0000
,值为128
[34 5 67 104 105 110 97]
这一段34
由 StringMessage.Home 的 fileNumber 4 和 wireType 2 (string) 构成:
(4<<3|2) = 34
5
表示接下来5个字节构成字符串67 104 105 110 97
是China
的 ASCII 编码
- 关于 msg2
使用 StringMessage 的序列化结果,反序列化到 StringMessage2, 虽然 StringMessage2 中不包含 filedNumber 为2, 3, 4
的字段,但是 StringMessage2 的实例 msg2 中依然可以在其中保留相关字段的内容。看一下 protobuf 根据 .proto 生成的 StringMessage2 的定义:
这些未在 StringMessage2 中定义的内容,就保存在type StringMessage2 struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Val string `protobuf:"bytes,1,opt,name=val,proto3" json:"val,omitempty"` }
unknownFields
字段里,重新序列化后,可以得到跟原始内容相同的序列化结果。