protobuf文档翻译-安装,数据格式及编码规范

Install

Download protobuf: https://github.com/protocolbuffers/protobuf/releases

unzip protoc-3.8.0-linux-x86_64.zip 
sudo cp -r include/* /usr/local/include/
sudo cp bin/protoc /usr/local/bin/

Download Go support for protobuf:

go get -u github.com/golang/protobuf/protoc-gen-go

Download faster and also more customizable Go support:

go get github.com/gogo/protobuf/protoc-gen-gofast

Proto2.0

每个proto文件中可以定义一组message。为了防止依赖关系等泛滥,一般只在一个proto文件中定义一组相关联的message。

定义一个message

message SomeMessage {
    [required|optional] type fieldName = fieldNumber [default=10];
}

fieldNumber 从1开始。

注释

注释的语法和C++相同:

/* 这是一段注释 */
// 这也是一段注释

保留字段(Reserved Fields)

当一个字段被删除后,后续的用户可以重新使用这个字段数字或名字, 就导致proto迁移过程中可能造成一些潜藏的bug。可以通过保留声明他们,
将来如果有用户重用了这些字段,就会报错:

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

数据类型

proto 注释 Go Python C++ Java
double *float64 float double double
float *float32 float float float
int32 编码长度是可变的,不能用于编码负数。如果要用于负数,应当使用sint32 *int32 int int32 int
int64 类似int32,应当使用sint64编码负数 *int64 int/log int64 long
uint32 变长编码 *uint32 int/long uint32 int
uint64 变长编码 *uint64 int/long uint64 long
sint32 变长编码 *int32 int int32 int
sint64 变长编码 *int64 int/long int64 long
fixed32 4字节,比uint32更适合编码超过2^28的数字 *uint32 int/long uint32 int
fixed64 8字节,2^56 *uint64 int/long uint64 long
sfixed32 4字节 *int32 int int32 int
sfixed64 8字节 *int64 int/long int64 long
bool *bool bool bool boolean
string 必须是UTF-8编码或是7bit的ASCII *string unicode(Py2), str(Py3) string String
bytes 可以包含任意byte序列 []byte bytes string ByteString

注意: 当说到更适合,或应当使用时,并不是说这个类型的表达范围不能够比得上另一种类型,而是其编码长度更有效率。

可选字段和默认值

一个message中可以设定一个字段为optional,当消息解码时发现该字段缺席,就会按照默认值(default)给定值,否则给定该类型的默认值(类似Go),枚举类型则会使用第一个枚举值。

形如:

optional int32 result_per_page = 3 [default=10];

枚举类型

枚举类型使用的是32位整形数,如果使用了负数,编码效率会变低

enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
}

枚举类型中默认第二个值是不能重复的,如果要有重复,那么就要指定允许别名:

enum State {
    option allow_alias = true;
    UNKNOWN = 0;
    STARTED = 1;
    RUNNING = 1;
}

保留枚举

如果你删除了枚举中的一个值,那么以后别人就可以重新用它们。可以这样保留这些字段:

enum Foo {
    reserved 2, 15, 9 to 11, 40 to max;
    reserved "FOO", "BAR";
}

导入定义

import "myproject/other_proto.proto"
import public "new.proto"

要想让protoc找到它们,需要通过 -I 或 --proto_path 来定义,一般是将项目根目录作为 --proto_path 参数。

类型嵌套

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; // 从外部引用一个嵌套类型
}

Proto3.0

syntax = "proto3";

message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
}
  • singular: 0或1个该字段
  • repeated: 可以出现0到多个该字段

proto3中数字类型默认使用packed编码

类型更新

  • 不要更改任何现有字段的field number
  • 旧message的默认值应当保持不变
  • 字段可以移除,在该field number不再使用的前提下
  • int32/uint32/int64/uint64/bool 是兼容的,可以任意修改类型,但是可能会出现精度问题等
  • sint32与sint64 兼容,但不与其它类型兼容
  • string和bytes兼容,前提是它们都是有效的UTF-8编码
  • 如果bytes包含了编码的message,嵌套message与bytes兼容
  • fixed32 与 sfixed32/fixed64/sfixed64兼容
  • enum与 int32/uint32/int64/uint64兼容
  • 把单个字段放入一个新的oneof是安全且兼容的;把多个字段放入一个新的oneof 可能 是安全且兼容的,如果你确认没有代码同时设定这多个字段的前提下。 把任何字段放入一个现存的oneof是不安全兼容的。

未知字段

由于proto版本兼容问题,导致旧的废弃字段不能被使用,proto3开始时会直接丢弃它们,但从3.5开始将它们保留

任意类型

任意类型 Any 本质上就是序列化好的bytes类型, 使用该类型需要引入 google/protobuf/any.proto:

import "google/protobuf/any.proto";

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

Oneof

如果message中有若干字段,一次最多只会设置其中的一个字段,那么就可以通过oneof来约定这种关系,类似于C语言的union:

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

oneof 的向后兼容

如果检查一个oneof的值返回 None/NOT_SET, oneof可能没有设置值,也可能是版本不同,但我们无法断定这件事。

Maps

map<key_type, value_type> map_field = N;
  • map类型字段不可以是 repeated
  • 遍历顺序/传输序列化顺序是不可知的
  • 从map专程text格式时会对key做排序
  • 从传输数据传入或合并时,如果map中的key存在重复,使用最后一个,从text格式处理map时,如果key重复会导致失败
  • 如果只提供key但不提供值,那么序列化行为随语言而定

Map类型在传输时等价于:

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

repeated MapFieldEntry map_field = N;

Packages

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

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

在Go中这会被当成包名

Services

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

Options

options不会修改数据定义,但是会影响一些语言生成的行为等等。

optimize_for

这是一个文件级选项,用于设定生成代码的优化方向。

optimize_for [SPEED|CODE_SIZE|LITE_RUNTIME]

SPEED: 默认值,优先生成高效编解码的代码
CODE_SIZE: 利用反射等语言特性生成体积小的代码,但操作可能会更慢
LITE_RUNTIME: 使用一个体积更小,也更少特性(如反射)的运行时来进行编解码

deprecacted

字段选项,用于暗示字段已弃用,新代码不应当再使用它。Java中会加入@Deprecated,其它语言暂不支持。

自定义选项

protobuf允许自定义选项,虽然大部分情况用不上: https://developers.google.com/protocol-buffers/docs/proto.html#extensions

JSON

protobuf 提供了JSON输出格式,部分类型可能与想象有所不同:

proto3 JSON JSON Example Notes
enum string "FOO_BAR" 默认使用枚举的名字,不过处理时使用int值也是可以的
map<K,V> object 所有的key都会被转换成字符串
repeated V array [v1, ...] null 会被解析城空的列表
bytes base64 string "YWJjMT..." 编码时会自动使用带padding的标准Base64编码成字符串
int32/fixed32/uint32 number 1, -10, 0 JSON值会是一个10进制数字,解析时数字或者字符串都可以接受
int64/fixed64/uint64 string "1", "-10" 由于64位整数已经超出了JSON number的表示范围,编码解码都是用string
float/double number 1.1, -10.0, "NaN", "Infinity","-Infinity" 编码时会被做成number或特殊字符串,解码时数字或字符串都可以
Any object @type暗示了真实的数据类型,如果Any中包含了一个值是特定的原生JSON数据,则会直接被解析出来
Timestamp string "1972-01-01T10:00:20.021Z" RFC3339,尾部小数点后可以有0,3,6,9位数字
Duration string "1.0002s", "1s" 尾部必须是s结尾,前面可以使用很多小数精度,但精度范围要在纳秒范围内
struct object see struct.proto
包装类型(别名) 与实际对应的类型行为保持一致
Empty object {} 空对象
NullValue null JSON null

在渲染JSON时,默认proto3不会输出值与默认值相等的字段,除非特别通过option指定

使用protoc

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

编码规范

文件名使用蛇形:

lower_snake_case.proto

文件格式

  • 一行最长80字符
  • 2空格缩进

文件结构

  1. License header
  2. File overview
  3. Syntax
  4. Package
  5. Imports (sorted)
  6. File options
  7. Everything else

重复字段(数组)

使用复数形式:

repeated string keys = 1;
repeated MyMessage accounts = 17;

枚举

枚举使用C语言的标准形式:全大写,下划线

Service

Service使用CamelCase

需要避免的

required和groups,这是给proto2用的,proto3不需要

posted @ 2019-05-31 16:13  Ethan.Tang  阅读(811)  评论(0编辑  收藏  举报