protobuf相关使用 | 青训营笔记
这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
计划完成抖音项目,提供的接口为proto2格式,记录一下proto相关内容并转化成golang代码。
1 安装proto
# 下载安装包
$ wget https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protoc-3.20.1-linux-x86_64.zip
# 解压到 /usr/local 目录下
$ sudo 7z x protoc-3.20.1-linux-x86_64.zip -o/usr/local
如果能正常显示版本,则表示安装成功。
$ protoc --version
libprotoc 3.20.1
2 安装protoc-gen-go
使用protoc-gen-go插件将protoc文件转化为go代码
$ go get -u github.com/golang/protobuf/protoc-gen-go
3 例子
3.1 user.proto
以用户接口为例
syntax = "proto2"; //proto版本,此处使用proto2
package douyin.core; //包名称,用来防止不同的消息类型有命名冲突。
option go_package = "../pkg;message"; //生成go文件路径以及包名
message User {
required int64 id = 1; // 类型 | 后面的「1」为数字标识符,在消息定义中需要唯一
required string name = 2;
optional int64 follow_count = 3;
optional int64 follower_count = 4;
required bool is_follow = 5;
}
字段规则:
required
:字段只能也必须出现 1 次
optional
:字段可出现 0 次或1次
repeated
:字段可出现任意多次(包括 0)
3.2 生成go文件
使用以下命令将文件夹内所有文件生成go文件
$ protoc --go_out=. *.proto
根据上面定义的生成go文件路径,生成文件如下
├── pkg
│ └── user.pb.go
├── proto
└── user.proto
生成对应的结构体
type User struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id *int64 `protobuf:"varint,1,req,name=id" json:"id,omitempty"`
Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
FollowCount *int64 `protobuf:"varint,3,opt,name=follow_count,json=followCount" json:"follow_count,omitempty"`
FollowerCount *int64 `protobuf:"varint,4,opt,name=follower_count,json=followerCount" json:"follower_count,omitempty"`
IsFollow *bool `protobuf:"varint,5,req,name=is_follow,json=isFollow" json:"is_follow,omitempty"`
}
3.3 序列化与反序列化
直接使用proto的Marshal、Unmarshal方法即可
import (
"google.golang.org/protobuf/proto"
"testing"
)
func TestProto(t *testing.T) {
user := &message.User{
Id: new(int64),
Name: new(string),
IsFollow: new(bool),
}
*user.Id = 123
*user.Name = "someName"
*user.IsFollow = false
data, err := proto.Marshal(user)
if err != nil {
t.Errorf("Marshal error\n")
}
newUser := &message.User{}
err = proto.Unmarshal(data, newUser)
if err != nil {
t.Errorf("Unmarshal error\n")
}
if user.GetId() != newUser.GetId() {
t.Errorf("user:%+v,newUser:%+v\n", user, newUser)
}
}
运行测试
=== RUN TestProto
--- PASS: TestProto (0.00s)
PASS
ok TikTokLite/proto 0.002s
4 字段类型
proto类型 | go类型 | 备注 | proto类型 | go类型 | 备注 |
---|---|---|---|---|---|
double | *float64 | float | *float32 | ||
int32 | *int32 | int64 | *int64 | ||
uint32 | *uint32 | uint64 | *uint64 | ||
sint32 | *int32 | 适合负数 | sint64 | *int64 | 适合负数 |
fixed32 | *uint32 | 固长编码,适合大于2^28的值 | fixed64 | *uint64 | 固长编码,适合大于2^56的值 |
sfixed32 | *int32 | 固长编码 | sfixed64 | *int64 | 固长编码 |
bool | *bool | string | *string | UTF8 编码,长度不超过 2^32 |
proto2版本生成的类型均为指针类型,proto3版本生成的类型均为值类型
5 一些问题
5.1 生成go文件报错 protoc-gen-go: program not found or is not executable
go get安装时会安装在$GOPATH/go/bin目录下
执行 cp protoc-gen-go /usr/local/bin/ 即可
5.2 proto import
例如video需要引入user的信息
message Video {
required User author = 1; //视频作者信息
}
需要在前面import user文件
import "user.proto";
message Video {
required douyin.core.User author = 1; //douyin.core为user的package名称
}
若video与user在同一个package内则不用添加包名称
import "user.proto";
package douyin.core;
message Video {
required User author = 1; //douyin.core为user的package名称
}
vscode proto插件import会报错,好像并不用管他,也没找到解决办法。。