gRPC之proto语法

本文描述如何使用proto3语法去构造你的数据结构,对官方文档不完全译文,只是摘出本人需要的部分来简单翻译官网地址,如果你无法进入官网链接请自行"跳墙"-_-.

目录
  • 1 定义消息类型
    • 1.1 指定字段类型
    • 1.2 分配标量
    • 1.3 指定属性规则
    • 1.4 添加更多的消息类型
    • 1.5 添加注释
    • 1.6 保留属性
  • 2 数据类型
  • 3 默认值
  • 4 枚举
  • 5 引用其他的消息类型
    • 5.1 导入其他proto中定义的消息
  • 6 内嵌类型
  • 8 包
  • 9 服务定义
  • 10 选项

1.定义消息类型

让我们先看一个 proto3 的查找请求参数的消息格式的例子,这个请求参数例子模仿分页查找请求,他有一个请求参数字符串,有一个当前页的参数还有一个每页返回数据大小的参数,proto文件内容如下:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 第一行的含义是限定该文件使用的是proto3的语法,如果没有 syntax = "proto3";

  • SearchRequest定义有三个承载消息的属性,每一个被定义在SearchRequest消息体中的字段,都是由数据类型和属性名称组成。

1.1 指定字段类型

在上面的例子中,所有的属性都是标量,两个整型(page_number、result_per_page)和一个字符串(query),你还可以在指定复合类型,包括枚举类型或者其他的消息类型。

1.2 分配标量

就像所看见的一样,每一个被定义在消息中的字段都会被分配给一个唯一的标量,这些标量用于标识你定义在二进制消息格式中的属性,标量一旦被定义就不允许在使用过程中再次被改变。标量的值在1~15的这个范围里占一个字节编码(详情请参看 谷歌的 Protocol Buffer Encoding )。

1.3 指定属性规则

消息属性规则如下:

  • singular: 一个正确的消息可以有零个或者多个这样的消息属性(但是不要超过一个).
  • repeated: 这个属性可以在一个正确的消息格式中重复任意次数(包括零次),
    在proto3中,标量数字类型的重复字段默认使用压缩编码

1.4 添加更多的消息类型

在一个proto文件中可以定义多个消息类型,你可以在一个文件中定义一些相关的消息类型,上面的例子proto文件中只有一个请求查找的消息类型,现在可以为他多添加一个响应的消息类型,具体如下:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
    ....
}

1.5 添加注释

proto文件中的注释使用的是c/c++中的单行注释 // 语法风格。
如下:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // 当前页数
  int32 result_per_page = 3;  // 每页数据返回的数据量

1.6 保留属性

为了避免在加载相同的.proto的旧版本,包括数据损坏,隐含的错误等,这可能会导致严重的问题的方法是指定删除的字段的字段标签(和/或名称,也可能导致JSON序列化的问题)被保留。 如果将来的用户尝试使用这些字段标识符,协议缓冲区编译器将会报错。


保留字段的使用例子:

message Foo {
  reserved 2;
  reserved "foo", "bar";
}

上述例子定义保留属性为"foo", "bar",定义保留属性位置为2,即在2这个位置上不可以定义属性,如:string name=2;是不允许的,编译器在编译proto文件的时候如果发现,2这个位置上有属性被定义则会报错。

2 数据类型



一个信息标量具有如下表格所示的数据类型,下表主要是对.proto文件的值类型和java的值类型的对照表

.proto Type Java Type
double double
float float
int32 int
int64 long
uint32 int
uint64 long
sint32 int
sint64 long
fixed32 int
fixed64 long
sfixed32 int
sfixed64 long
bool boolean
string String
bytes ByteString

详情参看官方文档

3 默认值

当proto消息被解析成具体的语言的时候,如果消息编码没包含特定的元素,则消息对象中的属性会被设置默认值,这些默认值具体如下:

  • string类型,默认值是空字符串,注意不是null
  • bytes类型,默认值是空bytes
  • bool类型,默认值是false
  • 数字类型,默认值是0
  • 枚举类型,默认值是第一个枚举值,即0
  • repeated修饰的属性,默认值是空(在相对应的编程语言中通常是一个空的list).

4 枚举

proto允许你在定义的消息类型的时候定义枚举类型,如下例,在消息类型中定义并使用枚举类型:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}

如上例中所示,Corpus枚举类型的第一个枚举值是0,每一个枚举值定义都会与一个常量映射,而这些常量的第一个常量值必须为0,原因如下:

  • 必须有一个0作为值,以至于我们可是使用0作为默认值
  • 第一个元素的值取0,用于与第一个元素枚举值作为默认值的proto2语义兼容


    枚举类型允许你定义别名,别名的作用是分配不中的标量,使用相同的常量值,使用别名只需要在定义枚举类型的第一行中添加allow_alias选项,并将值设置为true即可,如果没有设置该值就是用别名,在编译的时候会报错。


    官网例子如下:
enum EnumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}
enum EnumNotAllowingAlias {
  UNKNOWN = 0;
  STARTED = 1;
  //如果解除这个注释编译器在编译该proto文的时候会报错
  // RUNNING = 1;  
}

proto支持的枚举值的范围是32位的整形,即Java 中的int类型,其他请参看官网。

5 引用其他的消息类型

你可以在定义消息类型的时候饮用其他已经定义好的消息类型作为新消息类型的属性,官网例子如下:

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

在上面的消息例子中,SearchResponse这个响应消息类型的属性results,返回的是一个Result类型的消息列表。

5.1 导入其他proto中定义的消息

在上面的例子中,Result和SearchResponse消息类型被定义在同一个.proto文件中,如果把他们分成两个文件定义,应该如何引用呢?


proto中为我们提供了import 关键字用于引入不同.proto文件中的消息类型,你可以在你的.proto文件的顶部加入如下语句因为其他.proto文件的消息类型:
import "myproject/other_protos.proto";
例子:

  • 文件名称search_response.proto
syntax = "proto3";
import "test/result.proto";
package test1;

message SearchResponse {
  //包名.消息名
  repeated test2.Result results = 1;
}
  • 文件名称result.proto,在与search_response.proto同级目录的test下
syntax = "proto3";
package test2;

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}

如果两个.proto文件在同一个目录下直接这样import "result.proto";倒入即可。

6 内嵌类型

我们还可以在消息类型中定义消息,例子如下:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}

在上面的例子中在SearchResponse消息体中定义了一个Result消息并使用。


如果想在其他的消息体引用Result这个消息,可以Parent.Type这样引用,例子:

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}

消息还可以深层的嵌套定义,如下例子:

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}

7 Map

proto支持map属性类型的定义,语法如下:
map<key_type,value_type> map_field = N;
key_type可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型,枚举类型也是不合法的key类型),value_type可以是任何类型的数据。


map更具体的使用方式参看API

8 包

可以为proto文件指定包名,防止消息命名冲突。


例子如下:

package foo.bar;
message Open { ... }

当你在为消息类型定义属性的时候,你可以通过命名.类型的形式来使用已经定义好的消息类型,如下:

Message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

9 服务定义

如果你想在RPC中使用已经定义好的消息类型,你可以在.proto文件中定一个消息服务接口,protocol buffer编译器会生成对应语言的接口代码。

  • 接口定义例子:
service SearchService {
    //  方法名  方法参数                 返回值
    rpc Search(SearchRequest) returns (SearchResponse); 
}

10 选项

下面只列出java的.proto文件常用的一下选贤,其他选项前参看官网文档

  • java_package(文件选项):指定生成的java类所在的包, 如果在.proto文件中没有提供明确的java_package选项,那么默认情况下,将使用proto包。如果没有生成java代码该选项默认是不生效的。
    option java_package = "org.example.foo";

  • java_multiple_files(文件选项):指定在proto文件中定义的所有消息、枚举和服务在生成java类的时候都会生成对应的java类文件,而不是以内部类的形式出现。
    option java_multiple_files = true;

  • java_outer_classname(文件选项):指定生成的java类文件名称,如果不指定则会默认使用.proto文件的文件名称,如果没有生成java类文件,则该选项不会生效 <span id="1">Hello World</span>。
    option java_outer_classname = "HelloWorld";

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 142,811评论 1 300
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 61,236评论 1 256
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 94,386评论 0 212
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 41,004评论 0 175
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 48,779评论 1 255
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 38,776评论 1 176
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 30,393评论 2 271
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,161评论 0 166
  • 想象着我的养父在大火中拼命挣扎,窒息,最后皮肤化为焦炭。我心中就已经是抑制不住地欢快,这就叫做以其人之道,还治其人...
    爱写小说的胖达阅读 28,979评论 6 229
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 32,511评论 0 213
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,296评论 2 215
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 30,637评论 1 229
  • 白月光回国,霸总把我这个替身辞退。还一脸阴沉的警告我。[不要出现在思思面前, 不然我有一百种方法让你生不如死。]我...
    爱写小说的胖达阅读 24,242评论 0 31
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,121评论 2 213
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 31,512评论 3 204
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,619评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,013评论 0 167
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 33,525评论 2 230
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 33,615评论 2 231

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,002评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,072评论 6 345
  • 由于工程项目中拟采用一种简便高效的数据交换格式,百度了一下发现除了采用 xml、JSON 还有 ProtoBuf(...
    黄海佳阅读 47,872评论 1 23
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,344评论 6 13
  • 自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个平台产生了浓...
    taony阅读 1,355评论 0 42