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 如下
    syntax = "proto3";
    
    message StringMessage {
      string value = 1;
      int32 count = 2;
      int64 num = 3;
      string home = 4;
    }
    
    message StringMessage2 { string val = 1; }
    
    使用 protoc 将 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 99abc 的 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 97China 的 ASCII 编码
    • 关于 msg2
      使用 StringMessage 的序列化结果,反序列化到 StringMessage2, 虽然 StringMessage2 中不包含 filedNumber 为 2, 3, 4 的字段,但是 StringMessage2 的实例 msg2 中依然可以在其中保留相关字段的内容。看一下 protobuf 根据 .proto 生成的 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"`
      }
      
      这些未在 StringMessage2 中定义的内容,就保存在 unknownFields 字段里,重新序列化后,可以得到跟原始内容相同的序列化结果。
posted on 2022-07-10 20:05  HorseShoe2016  阅读(224)  评论(0编辑  收藏  举报