Protocol Bufffers 协议要点记录:proto3、proto2

本篇目录

proto3 语法要点

当前建议使用 proto3,Language Guide (proto3)

proto3 需要在文件开头声明语法版本为 proto3:

syntax = "proto3";

proto3 去掉了 requiredoptional,保留了 repeated,原因说明 why messge type remove ‘required,optional’?

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;                // 不需要指定 required 或者 optional,proto3 全部当作 optional 处理 
  string title = 2;
  repeated string snippets = 3;
}

提供了 一个 Any 类型:

import "google/protobuf/any.proto";

message ErrorStatus {
  string message = 1;
  repeated google.protobuf.Any details = 2;
}

其余部分基本继承了 proto2 的语法。

proto2 语法要点

PLanguage Guide(proto2) 介绍了 Proto2 的用法。

field 的编号:

  1. field 编号发布后不能再更改
  2. 编号 1~15 用一个字节描述,编号 16~2047 用两个字节
  3. 编号最小值是 1,最大 2^29-1
  4. 19000~19999 用于内部实现,不能使用

field 类型:

  1. repeated 修饰的 field,会保证参数顺序
  2. repeated 修饰的数值类型,因为历史原因默认不采用高效编码,启用高效编码需要明确指示 [packed = true]。
repeated int32 samples = 4 [packed = true];

用 reserverd 避免曾经使用过后来被删除的 field 的编号被再次使用:

message Foo {
  reserved 2, 15, 9 to 11;     // 使用编号
  reserved "foo", "bar";       // 或者名称,但是编号和名称不同同时混用
}

enum Foo {
  reserved 2, 15, 9 to 11, 40 to max;   // 用 max 表示最大值
  reserved "FOO", "BAR";
}

支持的类型:Scalar Value Types

double
float
int32
int64
uint32
uint64
sint32: 对负数的编码更高效
sint64: 对负数的编码更高效
fixed32:永远 4 字节,对应大于 2^28 的数值编码更高效
fixed64:永远 8 字节,对应大于 2^56 的数值编码更高效
sfixed32: 永远 4 字节
sfixed64: 永远 8 字节
bool:
string:UTF-8 或者 7-bit ASCII Text
bytes:

用 default 设置 optional field 缺失时的默认数值,如果未设置 default 使用类型默认值:

optional int32 result_per_page = 3 [default = 10];

enum 使用 32bit,使用 varint 编码,对负数编码效率低,不建议使用负数。

enum 可以独立定义,也可以在 message 内部定义:

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

import public 不支持 java,它的用途是让引用当前的文件,引用 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

proto2 可以引用 proto3 的定义,proto3 不能引用 proto2 中的 enum。

嵌套定义的引用方法:

message SearchResponse {
  message Result {
    required string url = 1;
    optional string title = 2;
    repeated string snippets = 3;
  }
  repeated Result result = 1;
}

message SomeOtherMessage {
  optional SearchResponse.Result result = 1;
}

group 不建议使用了,用嵌套定义替代 :

message SearchResponse {
  repeated group Result = 1 {      // group 不建议使用了
    required string url = 2;
    optional string title = 3;
    repeated string snippets = 4;
  }
}

更新接口定义注意事项 :

  1. 不要修改 field 的编号
  2. 新增的 field 使用 optional 或者 repeated
  3. 非 required 类型的 field 可以删除,但是 field num 不能再次分配给其它 field

extensions 用来预留扩展字段,其它 proto 文件可以自主设定预留字段的 field:

message Foo {
  // ...
  extensions 100 to 199;
}

// 另一个 proto 文件,扩展 Foo:
extend Foo {
  optional int32 bar = 126;     // 扩展的 field 不能是 oneof、map
}

oneof 中的 field 不能使用 requried、optional、repeated,但是可以使用内部使用了这些修饰符的 message

message SampleMessage {
  oneof test_oneof {
     string name = 4;              // 不能使用 requried、optional、repeated 修饰
     SubMessage sub_message = 9;   // SubMessage 内部的 field 可以用 requried、optional、repeated 修饰 
  }
}

map 的注意事项:

  1. map 的 key 不能是 float 和 bytes
  2. map 不能用 repeated, optional, required 修饰
  3. map 内的 key-value 顺序是不保证的

map 等价于下面的定义:

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

repeated MapFieldEntry map_field = N;

package 声明文件的包名:

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


// 另一个 proto 文件:
message Foo {
  ...
  required foo.bar.Open open = 1;
  ...
}

rpc 接口定义方法:

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

option 是 proto 文件的配置项,google/protobuf/descriptor.proto 示例了所有 option 的用法。

option 分为file-levelmessage-levelfield-level,分别在不同的位置使用。

file 级别的 option 用法:

option go_package = "google.golang.org/protobuf/types/descriptorpb";
option java_package = "com.google.protobuf";
option java_outer_classname = "DescriptorProtos";
option csharp_namespace = "Google.Protobuf.Reflection";
option objc_class_prefix = "GPB";
option cc_enable_arenas = true;

// descriptor.proto must be optimized for speed because reflection-based
// algorithms don't work during bootstrapping.
option optimize_for = SPEED;

message 级别的 option 用法:

message Foo {
  option message_set_wire_format = true;
  extensions 4 to max;
}

field 级别的 option 用法:

repeated int32 samples = 4 [packed = true];
optional int32 old_field = 6 [deprecated=true];

可以通过扩展 google.protobuf.XXXOptions 自定义 option:

import "google/protobuf/descriptor.proto";

extend google.protobuf.MessageOptions {   
  optional string my_option = 51234;       // 自定义 message-level option
}

message MyMessage {
  option (my_option) = "Hello world!";
}

代码生成命令,IMPORT_PATH 指定查找 proto 文件的路径:

protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto

IntelliJ IDEA 未识别 protobuf 的 import

安装了 protobuf 插件,IntelliJ IDEA 还是不能识别 protobuf 的 import:

IntelliJ IDEA 不能识别 protobuf 的 import

Protocol Buffer imports not recognized in Intellij中的方法有效:

解决protobuf import导入问题

参考

  1. 李佶澳的博客
  2. Protocol Buffer imports not recognized in Intellij
  3. PLanguage Guide(proto2)
  4. google/protobuf/descriptor.proto
  5. Language Guide (proto3)
  6. why messge type remove ‘required,optional’?

推荐阅读

赞助商广告

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  李佶澳的博客  小鸟笔记  软件手册  编程手册  运营手册  爱马影视  网络课程  奇技淫巧  课程文档  精选文章  发现知识星球  百度搜索 谷歌搜索