一年前学过对应的知识,终究是太潦草了,这几天网上学习了一下,重新写一下笔记。这里是protobuf和golang的结合
一、protobuf
protobuf实际上是一种类似json和gob之类的数据格式,也是grpc的御用格式吧(有自己的优势,这里我就不写了),其有着自己的定义方式,结合对应的指令可以实现转化成对应语言的操作。
二、写一个复杂一点的例子
先把我写的例子,整体粘过来,然后再做具体的分析
syntax="proto3";//顶格声明protobuf版本,默认是protobuf2
// 注意一下语句后面要加";",否则不识别
package user;//定义一下protobuf的包名,包名影响什么后续会写
import "role.proto";//这里编辑器报错不用管,应该是插件不能识别的问题
import "parent.proto";
import "google/protobuf/any.proto";//引入any类型
import "google/protobuf/timestamp.proto";//引入时间戳类型
/*
import public "some path"
这个public关键词用于控制这个包的依赖是否可以传递,例子如下a.proto:
import "b.proto"
import public "c.proto"index.proto:
import "a.proto"
那么这个index文件当中除了能使用a文件中定义的变量,还能使用c文件当中的遍历,但是b文件就不能使用*/
option go_package="../grpc;grpc";//规定生成文件的输出文件,同时规定对应文件package的名称
//这里指定的 out_path 并不是绝对路径,只是相对路径或者说只是路径的一部分,和 protoc 的 --go_out 拼接后才是完整的路径。所以我的侧率就是不写go_out// 这边我们写一个User结构体
//结构体的名称我们采取的是官网上的格式,注意和golang默认格式区分
// 具体的protobuf基础类型和对应语言之间对应表,参见https://protobuf.dev/programming-guides/proto3/#specifying-field-rulesmessage TestUser{// 保留字段和保留数字,这个用法我不是很懂,我看的资料显示如下/*如果一个字段不再需要,如果删除或者注释掉,则其他人在修改时会再次使用这些字段编号,那么旧的引用程序就可能出现一些错误,所以使用保留字段,保留已弃用的字段编号或字段名 我个人觉得这应该涉及到可拓展性的问题(难道更改的时候不会去重新生成对应的文件吗)*/reserved "hh";reserved 99 to 100;string name=1;uint64 id=2;optional float height=3;//默认字段都是必填,optional表示可选double money=4;bool merried=5;role.Role role=6;//这个就是role包引入的枚举类型,枚举定义在message内部就是独占枚举google.protobuf.Any other_msg=7;//any类型oneof child_name{string son_name=8;string daughter_name=9;//暂时看起来不同于枚举,oneof控制的事不同字段只能选一个}repeated string hobby=10;//可重复字段,应该会生成一个切片//内嵌字段,注意tag只是在同一个message内不能重复,内嵌的字段不算// 内嵌的字段是能单独拿出来用的,比如在另一个字段中,可以使用TestUser.GameCharacter// 注意这里的行为只是定义,要使用可以如下这样写//repeated GameCharacter game_character =100;message GameCharacter{string name=1;uint64 character_id=2;}// 创建一个mapmap<string,parent.Parent> parents=11;// 创建一个时间戳类型google.protobuf.Timestamp birthday=12;
}
1、前两个声明
syntax="proto3";//顶格声明protobuf版本,默认是protobuf2
第一个是版本声明
package user;//定义一下protobuf的包名
包名的声明,后面引入外部文件的时候会用大
2、四个import
前两个import引入了本报之外的其他两个文件,这两个应用的文件内容如下:
//role.proto
syntax="proto3";
package role;
option go_package="../grpc;grpc";
enum Role{NORMAL_USER=0;VIP_USER=1;BOSS=3;
};
//parent.proto
syntax="proto3";
package parent;
option go_package="../grpc;grpc";
message Parent{string name=1;uint32 age=2;
}
这两个引用在下面的TestUser之中的role字段和map中被使用,主要是我用来测试外部引入的。
后面两个import则是引入了官网定义的两个类型Any和Timestamp,也在下面的TestUser结构中使用
import "google/protobuf/any.proto";//引入any类型
import "google/protobuf/timestamp.proto";//引入时间戳类型
3、import public
引入前面可以加入public关键词,具体用法可以看这段
import public "some path"
这个public关键词用于控制这个包的依赖是否可以传递,例子如下//a.proto:
import "b.proto"
import public "c.proto"//index.proto:
import "a.proto"
那么这个index文件当中除了能使用a文件中定义的变量,还能使用c文件当中的遍历,但是b文件就不能使用
4、go_package
这个控制转化后的go文件的生成地址和生成go文件的package名,比如我这里的写法就是
option go_package="../grpc;grpc";//规定生成文件的输出文件,同时规定对应文件package的名称
生成的文件放到上级的grpc文件夹中,package是grpc,两个之间以“;”这格符号隔开,需要注意这个生成文件的存放路径需要和protoc指令中的“--go_out=“值相拼接,我为了不出错,都是只写go_package,"--go_out"的内容我是不写的
5、具体的TestUser字段
这里需要注意一下大小写的问题,message类似于结构体,他的名称是开头双大写,而其里面的字段则是小写,用“_”连接。
一下基础的类型比如string等等,可以查看对应的protobuf基础类型和对应语言之间对应表,参见https://protobuf.dev/programming-guides/proto3/#specifying-field-rules。
这里还展示了其他操作
optional:可选关键字
enum:这里是外部引入的role.Role类型
any:google.protobuf.Any,也是外部引入的
oneof:字段二选一,不同于枚举,这里的例子就是孩子的名字要么是男孩名字,要么是女孩名字
repeated:多次关键词,比如例子里的hobby字段,实际上会形成一个数组/切片(实际上字段名称写成hobbies比较好)
map类型:这里我们生成的是一个<string,parent.Parent>的键值对,其中parent.Parent是外部引入
时间戳类型:google.protobuf.Timestamp也是一个外部引入
6、内嵌message和enum
朋友们应该注意到了,我在TestUser之中还声明了一个GameCharacter(注意只是申明,要在TestUser使用还要显式挂载),也能声明一个枚举,有需要的时候可以在外部使用
三、生成一下protobuf
首先要安装一下protoc-gen-go生成工具来使用protoc指令
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
控制台使用如下指令查看是否安装成功
protoc --version
其次在项目中引入相关包
go get -u google.golang.org/protobuf
注意区分这次学习只生成protobuf,并不生成grpc的客户端和服务端,也就是这次只会生成对应的结构体
进入protobuf原始文件所在文件夹,控制台使用如下指令生成:
protoc --go_out=. *.proto
protoc指令其实有--proto_path和--go_out两个参数,第一个参数告知protobuf文件所在文件夹,但是由于路径我这边怎么写都不对,所以我直接进入了protobuf所在文件夹,这样就不写这个参数了。
--go_out=.这个写法就是让文件中go_package选项决定输出文件何在。
*.proto表示处理所有文件,当然你也可以指定具体文件名,中间用空号隔开。
最后就会在grpc文件夹之中生成parent.pb.go/user.pd.go/role.pd.go三个文件,具体文件的里的实现,可以自己去看,有些写法还是很有意思的。
加下来会加上grpc包,除了protobuf文件之外,还会有对应的客户端