Protocol Buffers 的使用简记

版本

Proto3

命名风格

字段名风格用下划线

定义一个消息类型

  1. 简单示例文件 example.proto

    syntax = "proto3";
    
    /* 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.
    }
    
    message SearchResponse {
     ...
    }
    
  • 首行表示版本,必须放在第一行空格、没有注释!
  • message 表示这是一个消息结构。
  • SearchRequest 消息名字。
  • int32string 指定消息类型。
  • 字段后面的1、2、3 是字段标识符,存储的对象的字段都是用数字表示,而不是字段名。这样可以节省内存,速度快等特点。1-15 数字编码后占1个字节,16-2047编码后占两个字节。190000 -19999 是保留数字,不能使用。
  • 字段有两种格式 : singularrepeatedsingular 表示该类型字段可以有零个或者一个,但不能超过一个。repeated 类似数组,表示该类型的字段零个或者多个。
  • 单行注释风格 : // ,多行注释: /* ... */

保留字段

当升级协议的时候,可能会删除一些旧的字段。 这样有可能新定义的字段名或者数字会和旧的代码冲突。为了兼容之前的版本或者考虑到未来的使用,有些字段名或者数字可以用 reserved 保留。

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

字段类型对应不同语言的输出

.proto TypeNotesC++ TypeJava TypePython Type[2]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[3]int64Bignumlonginteger/string[5]Int64
uint32Uses variable-length encoding.uint32int[1]int/long[3]uint32Fixnum or Bignum (as required)uintintegerint
uint64Uses variable-length encoding.uint64long[1]int/long[3]uint64Bignumulonginteger/string[5]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[3]int64Bignumlonginteger/string[5]Int64
fixed32Always four bytes. More efficient than uint32 if values are often greater than 228.uint32int[1]int/long[3]uint32Fixnum or Bignum (as required)uintintegerint
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 256.uint64long[1]int/long[3]uint64Bignumulonginteger/string[5]Int64
sfixed32Always four bytes.int32intintint32Fixnum or Bignum (as required)intintegerint
sfixed64Always eight bytes.int64longint/long[3]int64Bignumlonginteger/string[5]Int64
boolboolbooleanboolboolTrueClass/FalseClassboolbooleanbool
stringA string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot be longer than 2^32.stringStringstr/unicode[4]stringString (UTF-8)stringstringString
bytesMay contain any arbitrary sequence of bytes no longer than 2^32.stringByteStringstr[]byteStringString (ASCII-8BIT)ByteStringstring
  • 调用 set 方法的时候都会进行类型检查。

类型默认值

  1. 如果一个字段的值为默认值,那么序列化的时候,默认值不会被序列化。
  2. singular 字段不能区域是否字段被设置,原因见1。
  3. 参考字段默认值:
    strings : 空字符串。
    bytes : 空字节。
    bools: false
    numberic: 0。
    enums : 第一个定义的枚举值,必须是 0。
    repeated 字段为空列表。

枚举类型

  1. 基本格式
    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.
     }
    }
    
  • 允许同一个值的字段名不同(别名), 使用 allow_alias
  • 每个枚举类型的第一个值必须是0 (为了兼容 proto2)。
  • 枚举常量的范围必须在 32 位整数能够表示的范围之内。
  1. 保留值

    enum Foo {
      reserved 2, 15, 9 to 11, 40 to max;
      reserved "FOO", "BAR";
    }
    
  • 字段名和数字的保留值不能写在同一行。
  1. 文章推荐

使用其他的 message

  1. 简单示例
    message SearchResponse {
      repeated Result results = 1;
    }
    
    message Result {
      string url = 1;
      string title = 2;
      repeated string snippets = 3;
    }
    
  2. 导入其他 protomessage,使用 import 关键字。
    import "myproject/other_protos.proto";
    
  3. 使用 -I--proto_path 指定搜索目录,默认在编译的目录搜索。
  4. 可以在 proto3 导入 proto2message ,不建议混用。

嵌套调用

  1. 简单示例

    // 内部类嵌套
    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;
        }
      }
    }
    

协议更新的规则

  1. 不要改变现有字段的数值。
  2. 老的代码会忽略新代码新增的字段,不影响旧代码解析新数据。
  3. 可以移除或者重命名字段名,建议增加 reserved 保留使用过的字段名或者值。
  4. int32uint32int64uint64bool 之间可以相互转换。
  5. sint32sint64 彼此兼容,但是不和其他的整数类型兼容。
  6. 只要都是 UTF-8 编码,stringbytes 是相互兼容的。
  7. fixed32sfixed32 兼容,fixed64sfixed64 兼容。
  8. 将一个 singlular 字段移到一个新的 oneof 是安全的。将任何一个 singular 移到一个旧的 oneof 是不安全的。

Any

  1. Any 的使用

    import "google/protobuf/any.proto";
    
    message ErrorStatus {
      string message = 1;
      repeated google.protobuf.Any details = 2;
    }
    
    • 使用 Any 需要导入 import google/protobuf/any.proto
    • 二进制格式存储,可以包含任意的序列化的消息。type.googleapis.com/_packagename_._messagename_. 解析消息类型的用途。
  2. import google/protobuf/any.proto 的实现

    syntax = "proto3";
    
    package google.protobuf;	
    ...
    message Any {
        string type_url = 1;
        bytes value = 2;
    }	
    

oneof

  1. 示例用法

    message SampleMessage {
      oneof test_oneof {
        string name = 4;
        SubMessage sub_message = 9;
      }
    }
    
  • maprepeated 不能放在 oneof 里面。
  • oneof 设置了默认值之后,序列化的时候,默认值也包括在序列化数据中。
  • 移动一个 singular 字段到一个新的 oneof, 不会有问题。
  • 从一个现有的 oneof 移入或者移除字段可能丢失一些信息。
  • 从一个 oneof 中删除一个字段,然后再加回来,可能导致已序列化的数据丢失。
  1. 操作导致的 crash
    SampleMessage message;
    SubMessage* sub_message = message.mutable_sub_message();
    message.set_name("name");      // Will delete sub_message
    sub_message->set_...            // Crashes here
    
  • 由于设置了 set_name 了,sub_message 被删除,此时操作引起了 crash

map

  1. 简单声明

    map<key_type, value_type> map_field = N;
    
  • key 不是是 enum 类型。
  • value 不能是 map 类型,其他都可以。
  • 不能加 repeated 字段修饰。
  • 两个 map 进行 merge 的时候,如果 key 值重复,操作会失败。
  1. map 的等价定义

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

Packages

  1. 作用: 避免命名冲突。

  2. 使用

    package foo.bar;
    message Open { ... }
    
    // 其他的 proto
    message Foo {
     ...
     foo.bar.Open open = 1;
     ...
    }
    
  • c++ :等价于 foo::bar
  • java : 如果没有指定 java_package,那么等价于指定包路径。
  • python: 没有作用。
  • go::如果没有指定 go_package,那么等价于指定包路径。
  • c#:如果没有指定 csharp_namespace, 那么等价于 Foo.Bar

定义 Services

  1. 简单定义

    service SearchService {
      rpc Search(SearchRequest) returns (SearchResponse);
    }
    

    学习 gRPC

转 Json

proto3JSONJSON exampleNotes
messageobject{"fooBar": v, "g": null, …}Generates JSON objects. Message field names are mapped to lowerCamelCase and become JSON object keys. If the json_name field option is specified, the specified value will be used as the key instead. Parsers accept both the lowerCamelCase name (or the one specified by the json_name option) and the original proto field name. null is an accepted value for all field types and treated as the default value of the corresponding field type.
enumstring"FOO_BAR"The name of the enum value as specified in proto is used. Parsers accept both enum names and integer values.
map<K,V>object{"k": v, …}All keys are converted to strings.
repeated Varray[v, …]null is accepted as the empty list [].
booltrue, falsetrue, false
stringstring"Hello World!"
bytesbase64 string"YWJjMTIzIT8kKiYoKSctPUB+"JSON value will be the data encoded as a string using standard base64 encoding with paddings. Either standard or URL-safe base64 encoding with/without paddings are accepted.
int32, fixed32, uint32number1, -10, 0JSON value will be a decimal number. Either numbers or strings are accepted.
int64, fixed64, uint64string"1", "-10"JSON value will be a decimal string. Either numbers or strings are accepted.
float, doublenumber1.1, -10.0, 0, "NaN", "Infinity"JSON value will be a number or one of the special string values “NaN”, “Infinity”, and “-Infinity”. Either numbers or strings are accepted. Exponent notation is also accepted. -0 is considered equivalent to 0.
Anyobject{"@type": "url", "f": v, … }If the Any contains a value that has a special JSON mapping, it will be converted as follows: {"@type": xxx, "value": yyy}. Otherwise, the value will be converted into a JSON object, and the "@type" field will be inserted to indicate the actual data type.
Timestampstring"1972-01-01T10:00:20.021Z"Uses RFC 3339, where generated output will always be Z-normalized and uses 0, 3, 6 or 9 fractional digits. Offsets other than “Z” are also accepted.
Durationstring"1.000340012s", "1s"Generated output always contains 0, 3, 6, or 9 fractional digits, depending on required precision, followed by the suffix “s”. Accepted are any fractional digits (also none) as long as they fit into nano-seconds precision and the suffix “s” is required.
Structobject{ … }Any JSON object. See struct.proto.
Wrapper typesvarious types2, "2", "foo", true, "true", null, 0, …Wrappers use the same representation in JSON as the wrapped primitive type, except that null is allowed and preserved during data conversion and transfer.
FieldMaskstring"f.fooBar,h"See field_mask.proto.
ListValuearray[foo, bar, …]
ValuevalueAny JSON value. Check google.protobuf.Value for details.
NullValuenullJSON null
Emptyobject{}An empty JSON object

proto 输出 json 的的时候,proto3 可能会有下列的行为:

  • 忽略值为默认值的字段。
  • 忽略未知字段。
  • 输出字段转为驼峰命名法。
  • 忽略枚举。
  • 输入的枚举值以字符串格式存储,比如: 1 => “1”。

Options

java相关

  1. java_package : 指定输出的 java 包名。
    option java_package = "com.example.foo";
    
  2. java_outer_classname: 输出的 .proto 的文件名。
    option java_outer_classname = "Ponycopter";
    
  3. java_multiple_files : 是否输出多个 .java 文件。如果为 true,每个 class/enum 自己一个文件。

编译优化

  1. SPEED(default) 运行速度快。

  2. CODE_SIZE: 编译的代码体积小,但是速度略有下降。

  3. LITE_RUNTIME : 运行速度能和 SPEED 一样, 体积也比较小,但是想反射和 descroptors 功能不能用了。

    option optimize_for = CODE_SIZE;
    

描述字段

  1. deprecated: 表明字段被废弃,不应咋使用。
    int32 old_field = 6 [deprecated = true];
    

下载

链接:https://pan.baidu.com/s/10lCpvwbXFnWyzhAIXorTzg
提取码:vzdw

[1] Protocol Buffers

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值