proto3 学习
由于实习项目中用的是 gRPC ,在学习 gRPC 之前,我们需要先学习一下 proto 语法
Proto Buffer 简介
proto Buffer 简单介绍
protocol buffer 是一个语言无关,平台无关,可扩展的结构化数据序列化方案,用于协议通信,数据存储和其它更多用途(这个部分在前面序列化章节的博客中说过);它像 xml, 但是更小[1],更快[2]并且更简单,一旦定义好数据如何构造,就可以使用特殊的生成的源代码来轻易的读写你结构化数据和从不同的数据流,用不同的语言,你甚至可以更新你的数据结构而不打破已部署的使用“旧有”格式编译的程序。
proto 的优势和不足
优点:更简单;更小;更快;更清晰;生成数据访问类,更容易编程使用
缺点:不适合建模基于文本的标志(如 HTML)文档,因为无法轻易的使用文本交替结构。只有当有消息定义(.proto 文件)时才有意义
proto 常用语法
默认值
当消息被解析时,如果被编码的消息没有包含特定的简单元素,被解析的对象对应的字段会被设置为默认值。默认值是和类型有关的:
string ------> "" (不是 null)
bytes-------->byte[0] (不是 null)
boolean----->false
数字型-------> 0
枚举类型----->0 (默认值是第一个定义的枚举值,而这个值必须是 0)
消息字段------> null
重复字段------->空(通常都是空列表)
注1:对于简单字段,当消息被解析后,是没有办法知道这个字段到底是有设置值然后恰巧和默认值相同(比如给一个 boolen 类型的值设置为 false);还是没有给这个字段设置值而取了默认值。例如:不要用一个 boolean 值设置为 false 时来切换某些行为,而你又不希望这个行为默认会发生。
注2:如果一个简单消息字段被设置为它的默认值,那这个值不会被序列化。
枚举
enum Status {
option allow_alias = true; RUNNING = 0; STOPED = 1;
HAULT = 2; // 设置别名,不会报错 BLOCKED = 2; } message machine { int32 MachineId = 1; Status Status = 2; }
如上所示,Status 枚举的第一个常量设置到 0:每个枚举定义必须包含一个映射到 0 的常量作为它的第一个元素。这是因为:
一:必须有一个 0 值,这样我们才能用 0 来作为数值默认值
二:0 值必须是第一个元素,兼容 proto2 语法,在 proto2 中默认值总是第一个枚举值
注:可以通过将相同值赋值给不同的枚举常量来定义别名。为此需要设置 allow_alias 选项为 true, 否则当发现别名时 protocol 编译器会报错。
包导入
我们可以在一个包中导入另外一个包中定义的类型,这样可以避免大量的冗余定义,而且我们也可以给公用的类型单独放在 public 包中,方便定义的统一。
syntax = "proto3"; package SayHi; option go_package="SayHi"; import "proto/greet.proto" service SayHiService { rpc SayHi(SayHiReq) returns (SayHiRsp); // 打声招呼 } message SayHiReq { proto.SayHelloReq Value = 1; } message SayHiRsp { proto.SayHelloRsp Value = 1; }
proto 文件编译
1,由于官方的 protoc 编译器中并不支持 Go 语言,所以首先下载 基于 Go 语言插件才能生成 Go 代码。命令如下:
$ go get github.com/golang/protobuf/protoc-gen-go
上述命令会在 GOPATH 下的 bin 目录下生成一个 protoc-gen-go 的可二进制文件(windows 系统)
2,下载官方的 protoc 编译器,下载地址:https://github.com/protocolbuffers/protobuf/releases ,可以下载 64位 protoc-win64.zip,解压后在 bin 目录下有 protoc 的二进制文件,将此文件拷贝到系统的环境变量中的 PATH 目录下(windows 系统可在 cmd 下输入命令 set 查看环境变量),当显示如下图所示时,表示设置成功:
3,然后编写一个 greet.proto 文件,例如
syntax = "proto3"; package greeter; option go_package="greeter"; service greetService { rpc SayHello(SayHelloReq) returns (SayHelloRsp); // 打声招呼 } message SayHelloReq { string Name = 1; } message SayHelloRsp { string Response = 2; }
4,执行 protoc 命令生成需要的语言代码,本文举例编译输出 Go 语言代码,命令如下:
$ protoc -I=. --go_out=plugins=grpc:. greet.proto
参数解释:-I : 表示原文件 greet.proto 的文件路径;--go_out[--java_out] 表示生成的语言类型;=. : 表示要生成的 greet.pb.go 的路径;
注:. 表示当前路径
注:此命令不能用 protoc -I=. --go_out=. greet.proto, 这样生成的 Go 语言代码是不完整的,缺少 server 和 client 端代码,具体原因未知。要使用protoc-gen-go内置的gRPC插件生成gRPC代码。
参考资料: