protocol-1(语法)

122 阅读7分钟

官网地址

  官网地址

protocol 3 语法

  文档地址

定义消息类型

  定义查询请求消息结构,query为查询条件,page_number为感兴趣的页面,result_per_page为感兴趣的页面结果数量。内容写入.proto文件中。

//定义proto语法版本,默认为proto2
syntax = "proto3";
//定义消息结构
message SearchRequest {
  //string 查询条件 字段序号为1
  string query = 1;
  //int 感兴趣的页面  字段序号为2
  int32 page_number = 2;
  //int 结果数量  字段序号为3
  int32 result_per_page = 3;
}

分配字段序号

  消息结构所有的字段,都有唯一的字段序号。在消息体中,通过二进制格式,识别字段序号。字段序号1到15,用一个字节标识字段序号和字段类型;16到2047,用两个字节标识字段序号和字段类型。在设计上,应该为频繁出现字段设计为1到15,为了后期扩展在设计上字段序号最好预留一些空间。   字段序号最小为1,最大为536,870,911(2的29次方减1)。19000 到 19999为预留字段,也不可以使用。

字段规则

  字段规则包含单数、重复数,单数为默认字段规则。

singular 单数
repeated 重复数

增加更加消息类型

  一个.proto文件中,可以定义多个消息体。

//定义proto语法版本,默认为proto2
syntax = "proto3";
//定义消息结构
message SearchRequest {
  //string 查询条件 字段序号为1
  string query = 1;
  //int 感兴趣的页面  字段序号为2
  int32 page_number = 2;
  //int 结果数量  字段序号为3
  int32 result_per_page = 3;
}

message SearchResponse{
}

文件注释

  .proto文件注释和java代码注释一致。

/* SearchRequest represents a search query, with pagination options to
 * indicate which results to include in the response. */

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // Which page number do we want?
  int32 result_per_page = 3;  // Number of results to return per page.
}

预定字段

  如果下游使用者对.proto字段进行了删除、注释,后期又做了一些更改,可能导致序列化失败。一种方法是通过制定占用某些字段序号、字段名称,使后期再次修改时,不可占用这些预定字段,否则编译失败。

message Foo {
  reserved 2, 15, 9 to 11;
  reserved "foo", "bar";
}

标量字段类型

  标量字段类型,如下所示。

.proto TypeNotesC++ TypeJava/Kotlin Type[1]Python Type[3]Go TypeRuby TypeC# TypePHP TypeDart Type
doubledoubledoublefloatfloat64Floatdoublefloatdouble
floatfloatfloatfloatfloat32Floatfloatfloatdouble
int32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.int32intintint32Fixnum or Bignum (as required)intintegerint
int64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
uint32Uses variable-length encoding.uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
uint64Uses variable-length encoding.uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.int32intintint32Fixnum or Bignum (as required)intintegerint
sint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
fixed32Always four bytes. More efficient than uint32 if values are often greater than 228.uint32int[2]int/long[4]uint32Fixnum or Bignum (as required)uintintegerint
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 256.uint64long[2]int/long[4]uint64Bignumulonginteger/string[6]Int64
sfixed32Always four bytes.int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64Always eight bytes.int64longint/long[4]int64Bignumlonginteger/string[6]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
stringA string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 232.stringStringstr/unicode[5]stringString (UTF-8)stringstringString
bytesMay contain any arbitrary sequence of bytes no longer than 232.stringByteStringstr (Python 2) bytes (Python 3)[]byteString (ASCII-8BIT)ByteStringstringList

字段默认值

  不同字段类型有不同的默认值。一旦消息解析,不保证字段一定明确序列化为默认值,也有可能完全没有序列化,这个需要看具体语言实现方式。

  • string 默认为空字符串
  • bytes 默认为空bytes
  • bools 默认为false
  • numeric 默认为0
  • enums 默认为第一个定一个的枚举,值必须为0
  • 消息体字段,则看具体语言默认值
  • 重复字段 默认值为空,一般为空的集合

枚举

  定义枚举,第一个值为默认值,并且必须为0。

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;
}

  定义枚举,可以定义别名,启用别名,则不同的值可以映射不同的枚举。

message MyMessage1 {
  enum EnumAllowingAlias {
    option allow_alias = true;//使用别名
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
  }
}
message MyMessage2 {
  enum EnumNotAllowingAlias {
    UNKNOWN = 0;
    STARTED = 1;
    // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
  }
}

使用其他类型作为字段

  消息体可以用其他消息体作为字段。其他消息体可以定义在同一个文件中,也可以定义不同的文件中。定义在不同的文件中,通过**import"myproject/other_protos.proto";**引入其他文件。

message SearchResponse {
  repeated Result results = 1;
}

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

  通过引用则可以使用其他文件的消息结构。但是import作用范围只在当前文件中,如果当前文件又被import,则另一个文件用不了当前文件的引用。需要import作用范围可以传递下去,则需要用import public

// new.proto
// All definitions are moved here
// old.proto
// This is the proto that all clients are importing.
import public "new.proto";
import "other.proto";
// client.proto
import "old.proto";
// You use definitions from old.proto and new.proto, but not other.proto

嵌套消息类型定义

  消息定义内部可以再次定义消息。

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
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;
    }
  }
}

更新消息

  如果消息类型不在符合你的要求,例如你需要额外的字段,或者删除某个字段,则需要修改协议文件。为了达到安全修改协议,需要遵守以下几个标准

  • 不要修改已存在的字段序号
  • 新增字段,老的协议序列化二进制可以被新的协议解析。老协议解析信的协议序列化二进制,会忽略新增字段。
  • 删除字段,新的协议不要再使用删除字段的字段序号
  • int32uint32int64uint64, 和 bool 相互兼容
  • sint32 和 sint64 相互兼容
  • stringbytes,utf8格式相互兼容
  • 单数字段转成oneof可以兼容,重复数转成oneof在确保只有序列化一个时可以兼容

不认识的字段

  不认识的字段为协议解析时不认识的字段。

oneof字段

  如果消息有多个字段,但是只有存在其中一个,则可以定义为oneOf。

message SampleMessage {
  oneof test_oneof {
    string name = 4;
    SubMessage sub_message = 9;
  }
}

map类型

  如果需要定义key-value类型,可以用map。key_type不可以为枚举类型。map类型不可以为重复数。

map<key_type, value_type> map_field = N;

  以下写法与map效果一致。

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;

package

  多个消息文件,消息名一致,可以通过设置package区分。java如果没有指定java_package,则默认为package

package foo.bar;
message Open { ... }
message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}

其他optional属性

  • java_package   包路径。
option java_package = "com.example.foo";
  • java_outer_classname

  • java_multiple_files

option java_multiple_files = true;//消息类是否生成多个文件

生成代码

下载路径

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto