yes风淘宝网站,商城系统,室内平面图在线制作网站,37网页游戏平台官网1、Protobuf语法
gRPC推荐使用proto3#xff0c;这里只介绍常用语法#xff0c;更多高级使用方法请参考官方文档#xff1a;
https://developers.google.com/protocol-buffers/doc
1.1 Message定义
一个message类型定义描述了一个请求或响应的消息格式#xff0c;可以包…1、Protobuf语法
gRPC推荐使用proto3这里只介绍常用语法更多高级使用方法请参考官方文档
https://developers.google.com/protocol-buffers/doc
1.1 Message定义
一个message类型定义描述了一个请求或响应的消息格式可以包含多种类型字段。
例如定义一个搜索请求的消息格式SearchRequest每个请求包含查询字符串、页码、每页数目。每个字段声明
以分号结尾。
syntax proto3;
message SearchRequest {string query 1;int32 page_number 2;int32 result_per_page 3;
}首行要求明确声明使用的protobuf版本为proto3如果不声明编译器默认使用proto2。本声明必须在文件的
首行。
一个.proto文件中可以定义多个message一般用于同时定义多个相关的message例如在同一个.proto文件中
同时定义搜索请求和响应消息
syntax proto3;
message SearchRequest {string query 1;int32 page_number 2;int32 result_per_page 3;
}
message SearchResponse {...
}1.2 字段类型声明
所有的字段需要前置声明数据类型上面的示例指定了两个数值类型和一个字符串类型。除了基本的标量类型还有
复合类型如枚举、map、数组、甚至其它message类型等后面会依次说明。
1.3 分配Tags
消息的定义中每个字段都有一个唯一的数值标签。这些标签用于标识该字段在消息中的二进制格式使用中的类
型不应该随意改动。其中[1-15]内的标识在编码时占用一个字节包含标识和字段类型。[16-2047]之间的标识
符占用2个字节。建议为频繁出现的消息元素分配[1-15]间的标签。如果考虑到以后可能或扩展频繁元素可以预
留一些。
最小的标识符可以从1开始最大到229 - 1或536,870,911。不可以使用[1900019999]之间的标识符
Protobuf协议实现中预留了这些标识符。在.proto文件中使用这些预留标识号编译时会报错。
1.4 字段规则 单数形态一个message内同名单数形态的字段不能超过一个 repeated前置repeated关键词声明该字段为数组类型 proto3不支持proto2中的required和optional关键字
repeated是protobuf中的一种限定修饰符从字面意思看有重复的意思实际上它就是用来指定某一个字段可
以存放同一个类型的多个数据(当然也可以是0个或者1个)相当于C中的vector或者Java中的List。
1.5 添加注释
向.proto文件中添加注释支持C/C风格双斜线//单行注释。
syntax proto3; // 协议版本声明
// SearchRequest 搜索请求消息
message SearchRequest {string query 1; // 查询字符串int32 page_number 2; // 页码int32 result_per_page 3; // 每页条数
}1.6 保留字段名与Tag
可以使用reserved关键字指定保留字段名和标签。
message Foo {reserved 2, 15, 9 to 11;reserved foo, bar;
}注意不能在一个reserved声明中混合字段名和标签。
1.7 .proto文件编译结果
当使用protocol buffer编译器运行.proto文件时编译器将生成所选语言的代码用于使用在.proto文件中定
义的消息类型、服务接口约定等。不同语言生成的代码格式不同 C每个.proto文件生成一个.h文件和一个.cc文件每个消息类型对应一个类。 Java生成一个.java文件同样每个消息对应一个类同时还有一个特殊的Builder类用于创建消息接口 Python: 姿势不太一样每个.proto文件中的消息类型生成一个含有静态描述符的模块该模块与一个元类 metaclass在运行时创建需要的Python数据访问类。 Go生成一个.pb.go文件每个消息类型对应一个结构体。 Ruby生成一个.rb文件的Ruby模块包含所有消息类型。 JavaNano类似Java但不包含Builder类。 Objective-C每个.proto文件生成一个pbobjc.h和一个pbobjc.m文件。 C#生成.cs文件包含每个消息类型对应一个类。
各种语言的更多的使用方法请参考官方API文档
https://developers.google.com/protocol-buffers/docs/reference/overview
1.8 基本数据类型
.protoCJavaPythonGoRubyC#doubledoubledoublefloatfloat64Floatdoublefloatfloatfloatfloatfloat32Floatfloatint32int32intintint32Fixnum or Bignumintint64int64longint/long[3]int64Bignumlonguint32uint32int[1]int/long[3]uint32Fixnum or Bignumuintuint64uint64long[1]int/long[3]uint64Bignumulongsint32int32intintint32Fixnum or Bignumintsint64int64longint/long[3]int64Bignumlongfixed32uint32int[1]intuint32Fixnum or Bignumuintfixed64uint64long[1]int/long[3]uint64Bignumulongsfixed32int32intintint32Fixnum or Bignumintsfixed64int64longint/long[3]int64BignumlongboolboolbooleanbooleanboolTrueClass/FalseClassboolstringstringStringstr/unicode[4]stringString(UTF-8)stringbytesstringByteStringstr[]byteString(ASCII-8BIT)ByteString
关于这些类型在序列化时的编码规则请参考Protocol Buffer Encoding
https://developers.google.com/protocol-buffers/docs/encoding
1.9 默认值 字符串类型默认为空字符串 字节类型默认为空字节 布尔类型默认false 数值类型默认为0值 enums类型默认为第一个定义的枚举值必须是0
针对不同语言的默认值的具体行为参考 generated code guide
https://developers.google.com/protocol-buffers/docs/reference/overview
1.10 枚举(Enum)
当定义一个message时想要一个字段只能是一个预定义好的值列表内的一个值就需要用到enum类型了。
示例这里定义一个名为Corpus的enum类型值并且指定一个字段为Corpus类型。
message SearchRequest {string query 1;int32 page_number 2;int32 result_per_page 3;// 定义enum类型enum Corpus {UNIVERSAL 0;WEB 1;IMAGES 2;LOCAL 3;NEWS 4;PRODUCTS 5;VIDEO 6;}Corpus corpus 4; // 使用Corpus作为字段类型
}注意每个enum定义的第一个元素值必须是0。
还可以通过给不同的enum元素赋相同的值来定义别名要求设置allow_alias选项的值为true否则会报错。
// 正确示例
enum EnumAllowingAlias {option allow_alias true; // 开启allow_alias选项UNKNOWN 0;STARTED 1;RUNNING 1; // RUNNING和STARTED互为别名
}
// 错误示例
enum EnumNotAllowingAlias {UNKNOWN 0;STARTED 1;RUNNING 1; // 未开启allow_alias选项编译会报错
}enum类型值同样支持文件级定义和message内定义引用方式与message嵌套一致。
1.11 使用其它Message
可以使用其它message类型作为字段类型。
例如在SearchResponse中包含Result类型的消息可以在相同的.proto文件中定义Result消息类型然后
在SearchResponse中指定字段类型为Result:
message SearchResponse {repeated Result results 1;
}
message Result {string url 1;string title 2;repeated string snippets 3;
}1.12 导入定义(import)
上面的例子中Result类型和SearchResponse类型定义在同一个.proto文件中我们还可以使用import语句
导入使用其它描述文件中声明的类型
import others.proto;默认情况只能使用直接导入的.proto文件内的定义。但是有时候需要移动.proto文件到其它位置为了避免
更新所有相关文件可以在原位置放置一个模型.proto文件使用public关键字转发所有对新文件内容的引
用例如
// new.proto
// 所有新的定义在这里// old.proto
// 客户端导入的原来的proto文件
import public new.proto;
import other.proto;// client.proto
import old.proto;
// 这里可以使用old.proto和new.proto文件中的定义但是不能使用other.proto文件中的定义。protocol编译器会在编译命令中 -I/--proto_path参数指定的目录中查找导入的文件如果没有指定该参数
默认在当前目录中查找。
1.13 Message嵌套
上面已经介绍过可以嵌套使用另一个message作为字段类型其实还可以在一个message内部定义另一个
message类型作为子级message。
示例Result类型在SearchResponse类型中定义并直接使用作为results字段的类型。
message SearchResponse {message Result {string url 1;string title 2;repeated string snippets 3;}repeated Result results 1;
}内部声明的message类型名称只可在内部直接使用在外部引用需要前置父级message名称如Parent.Type
message SomeOtherMessage {SearchResponse.Result result 1;
}支持多层嵌套
message Outer { // Level 0message MiddleAA { // Level 1message Inner { // Level 2int64 ival 1;bool booly 2;}}message MiddleBB { // Level 1message Inner { // Level 2int32 ival 1;bool booly 2;}}
}1.14 Map类型
proto3支持map类型声明
mapkey_type, value_type map_field N;
message Project {...}
mapstring, Project projects 1;key_type类型可以是内置的标量类型(除浮点类型和bytes) value_type可以是除map以外的任意类型 map字段不支持repeated属性 不要依赖map类型的字段顺序
1.15 包(Packages)
在.proto文件中使用package声明包名避免命名冲突。
syntax proto3;
package foo.bar;
message Open {...}在其他的消息格式定义中可以使用包名消息名的方式来使用类型如
message Foo {...foo.bar.Open open 1;...
}在不同的语言中包名定义对编译后生成的代码的影响不同 C 中对应C命名空间例如Open会在命名空间foo::bar中。 Java 中package会作为Java包名除非指定了option jave_package选项。 Python 中package被忽略。 Go 中默认使用package名作为包名除非指定了option go_package选项。 JavaNano 中同Java。 C# 中package会转换为驼峰式命名空间如Foo.Bar,除非指定了option csharp_namespace选项。
1.16 定义服务(Service)
如果想要将消息类型用在RPC(远程方法调用)系统中可以在.proto文件中定义一个RPC服务接口protocol编译
器会根据所选择的不同语言生成服务接口代码。例如想要定义一个RPC服务并具有一个方法该方法接收
SearchRequest并返回一个SearchResponse此时可以在.proto文件中进行如下定义
service SearchService {rpc Search (SearchRequest) returns (SearchResponse) {}
}生成的接口代码作为客户端与服务端的约定服务端必须实现定义的所有接口方法客户端直接调用同名方法向服
务端发起请求。
1.17 选项(Options)
在定义.proto文件时可以标注一系列的options。Options并不改变整个文件声明的含义但却可以影响特定环境
下处理方式。完整的可用选项可以查看google/protobuf/descriptor.proto。
一些选项是文件级别的意味着它可以作用于顶层作用域不包含在任何消息内部、enum或服务定义中。一些选
项是消息级别的可以用在消息定义的内部。
以下是一些常用的选项 java_package (file option)指定生成java类所在的包如果在.proto文件中没有明确的声明 java_package会使用默认包名。不需要生成java代码时不起作用。 java_outer_classname (file option)指定生成Java类的名称如果在.proto文件中没有明确声明 java_outer_classname生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如 foo_bar.proto生成的java类名为FooBar.java,不需要生成java代码时不起任何作用。 objc_class_prefix (file option): 指定Objective-C类前缀会前置在所有类和枚举类型名之前。没有默认 值应该使用3-5个大写字母。注意所有2个字母的前缀是Apple保留的。
1.18 基本规范 描述文件以.proto做为文件后缀除结构定义外的语句以分号结尾 结构定义包括message、service、enum rpc方法定义结尾的分号可有可无 Message命名采用驼峰命名方式字段命名采用小写字母加下划线分隔方式 message SongServerRequest {required string song_name 1;
}Enums类型名采用驼峰命名方式字段命名采用大写字母加下划线分隔方式 enum Foo {FIRST_VALUE 1;SECOND_VALUE 2;
}Service名称与RPC方法名统一采用驼峰式命名
1.19 编译
通过定义好的.proto文件生成Java, Python, C, Go, Ruby, JavaNano, Objective-C, or C# 代码需要安装编译
器protoc。参考Github项目google/protobuf(https://github.com/google/protobuf)安装编译器。
运行命令
protoc --proto_pathIMPORT_PATH --cpp_outDST_DIR --java_outDST_DIR --python_outDST_DIR --go_outDST_DIR --ruby_outDST_DIR --javanano_outDST_DIR --objc_outDST_DIR --csharp_outDST_DIR path/to/file.proto这里只做参考就好具体语言的编译实例请参考详细文档。 [Go generated code reference] https://developers.google.com/protocol-buffers/docs/reference/go-generated [C generated code reference] https://developers.google.com/protocol-buffers/docs/reference/cpp-generated [Java generated code reference] https://developers.google.com/protocol-buffers/docs/reference/java-generated [Python generated code reference] https://developers.google.com/protocol-buffers/docs/reference/python-generated [Objective-C generated code reference] https://developers.google.com/protocol-buffers/docs/reference/objective-c-generated [C# generated code reference] https://developers.google.com/protocol-buffers/docs/reference/csharp-generated [JavaScript Generated Code Guide] https://developers.google.com/protocol-buffers/docs/reference/javascript-generated [PHP Generated Code Guide] https://developers.google.com/protocol-buffers/docs/reference/php-generated 照着官方文档一步步操作不一定成功哦 1.20 更多 [Any类型] https://developers.google.com/protocol-buffers/docs/proto3#any [Oneof] https://developers.google.com/protocol-buffers/docs/proto3#oneof [自定义Options] https://developers.google.com/protocol-buffers/docs/proto.html#customoptions
这些用法在实践中很少使用这里不做介绍有需要请参考官方文档
https://developers.google.com/protocol-buffers/
1.21 Protobuf⇢Go的转换说明
这里使用一个测试文件对照说明常用结构的protobuf到go的转换。
示例test.proto
syntax proto3;
package test;
option go_package ./test;message Test {int32 age 1;int64 count 2;double money 3;float score 4;string name 5;bool fat 6;bytes char 7;enum Status {OK 0;FAIL 1;}Status status 8;message Child {string sex 1;}Child child 9;mapstring, string dict 10;
}service TestService {rpc Test(Request) returns (Response) {};
}message Request {string name 1;
}message Response {string message 1;
}$ protoc -I . --go_outpluginsgrpc:. test.proto# 树结构
$ tree demo
demo
├── go.mod
├── test
│ └── test.pb.go
└── test.proto1 directory, 3 files1.21.1 Package
在proto文件中使用package关键字声明包名默认转换成go中的包名与此一致如果需要指定不一样的包名可
以使用go_package选项
package test;
option go_package ./test;1.21.2 Message
proto中的message对应go中的struct全部使用驼峰命名规则。嵌套定义的messageenum转换为go之后
名称变为Parent_Child结构。
// Status 枚举状态
type Test_Status int32const (Test_OK Test_Status 0Test_FAIL Test_Status 1
)// Child 子结构
type Test_Child struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsSex string protobuf:bytes,1,opt,namesex,proto3 json:sex,omitempty
}// 整体结构
type Test struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsAge int32 protobuf:varint,1,opt,nameage,proto3 json:age,omitemptyCount int64 protobuf:varint,2,opt,namecount,proto3 json:count,omitemptyMoney float64 protobuf:fixed64,3,opt,namemoney,proto3 json:money,omitemptyScore float32 protobuf:fixed32,4,opt,namescore,proto3 json:score,omitemptyName string protobuf:bytes,5,opt,namename,proto3 json:name,omitemptyFat bool protobuf:varint,6,opt,namefat,proto3 json:fat,omitemptyChar []byte protobuf:bytes,7,opt,namechar,proto3 json:char,omitemptyStatus Test_Status protobuf:varint,8,opt,namestatus,proto3,enumtest.Test_Status json:status,omitemptyChild *Test_Child protobuf:bytes,9,opt,namechild,proto3 json:child,omitemptyDict map[string]string protobuf:bytes,10,rep,namedict,proto3 json:dict,omitempty protobuf_key:bytes,1,opt,namekey,proto3 protobuf_val:bytes,2,opt,namevalue,proto3
}除了会生成对应的结构外还会有些工具方法如字段的getter:
func (x *Test) GetAge() int32 {if x ! nil {return x.Age}return 0
}func (x *Test) GetCount() int64 {if x ! nil {return x.Count}return 0
}枚举类型会生成对应名称的常量同时会有两个map方便使用
// Enum value maps for Test_Status.
var (Test_Status_name map[int32]string{0: OK,1: FAIL,}Test_Status_value map[string]int32{OK: 0,FAIL: 1,}
)1.21.3 Service
TestService有一个方法Test接收一个Request参数返回Response。
// 客户端接口
type TestServiceClient interface {Test(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}// 服务端接口
type TestServiceServer interface {Test(context.Context, *Request) (*Response, error)
}生成的go代码中包含该Service定义的接口客户端接口已经自动实现了直接供客户端使用者调用服务端接口
需要由服务提供方实现。