doc: 阅读protobuf文档
This commit is contained in:
@@ -7,6 +7,39 @@
|
|||||||
- [Singular](#singular)
|
- [Singular](#singular)
|
||||||
- [repeated](#repeated)
|
- [repeated](#repeated)
|
||||||
- [map](#map)
|
- [map](#map)
|
||||||
|
- [Message Type Files Always have Field Presence](#message-type-files-always-have-field-presence)
|
||||||
|
- [well-formed messages](#well-formed-messages)
|
||||||
|
- [在相同`.proto`中定义多个message type](#在相同proto中定义多个message-type)
|
||||||
|
- [删除Fields](#删除fields)
|
||||||
|
- [reversed field number](#reversed-field-number)
|
||||||
|
- [reversed field names](#reversed-field-names)
|
||||||
|
- [what generated from `.proto`](#what-generated-from-proto)
|
||||||
|
- [Scalar Value Types](#scalar-value-types)
|
||||||
|
- [default field values](#default-field-values)
|
||||||
|
- [enumerations](#enumerations)
|
||||||
|
- [enum value alias](#enum-value-alias)
|
||||||
|
- [修改message Type需要遵循的原则](#修改message-type需要遵循的原则)
|
||||||
|
- [Unknown Fields](#unknown-fields)
|
||||||
|
- [unknown fields丢失](#unknown-fields丢失)
|
||||||
|
- [Any](#any)
|
||||||
|
- [OneOf](#oneof)
|
||||||
|
- [OneOf Feature](#oneof-feature)
|
||||||
|
- [向后兼容问题](#向后兼容问题)
|
||||||
|
- [Maps](#maps)
|
||||||
|
- [map feature](#map-feature)
|
||||||
|
- [向后兼容性](#向后兼容性)
|
||||||
|
- [package](#package)
|
||||||
|
- [Service In Rpc System](#service-in-rpc-system)
|
||||||
|
- [gRPC](#grpc)
|
||||||
|
- [json](#json)
|
||||||
|
- [Options](#options)
|
||||||
|
- [消息级别](#消息级别)
|
||||||
|
- [java\_package (file option)](#java_package-file-option)
|
||||||
|
- [java\_outer\_class\_name (file option)](#java_outer_class_name-file-option)
|
||||||
|
- [java\_multiple\_files (file option)](#java_multiple_files-file-option)
|
||||||
|
- [optimize\_for (file option)](#optimize_for-file-option)
|
||||||
|
- [deprecated (field option)](#deprecated-field-option)
|
||||||
|
- [生成代码](#生成代码)
|
||||||
|
|
||||||
|
|
||||||
# protobuf
|
# protobuf
|
||||||
@@ -61,13 +94,374 @@ field number被限制为`29bit`,故而field number的最大值为`536870911`
|
|||||||
- 如果field为一个message type,那么其行为和`optional`相同
|
- 如果field为一个message type,那么其行为和`optional`相同
|
||||||
- 如果field不是message,那么其有两种状态:
|
- 如果field不是message,那么其有两种状态:
|
||||||
- 如果field被设置为非默认值(non-zero),其会被序列化到wire中
|
- 如果field被设置为非默认值(non-zero),其会被序列化到wire中
|
||||||
- 如果field被设置为zero value,那么其不会被序列化到wire中
|
- 如果field被设置为默认值,那么其不会被序列化到wire中
|
||||||
|
|
||||||
> 相比于`implict`,更推荐使用`optional`,使用`optional`能更好与proto2相兼容
|
> 相比于`implict`,更推荐使用`optional`,使用`optional`能更好与proto2相兼容
|
||||||
|
|
||||||
|
> `optional`和`implicit`的区别是,如果scalar field被设置为默认值,在`optional`场景下,其会被序列化到wire中,而`implicit`则不会对其进行序列化
|
||||||
|
|
||||||
#### repeated
|
#### repeated
|
||||||
代表该field可以在消息中出现0次或多次,消息出现的顺序也将被维护
|
代表该field可以在消息中出现0次或多次,消息出现的顺序也将被维护
|
||||||
|
|
||||||
#### map
|
#### map
|
||||||
代表field为成对的键值对
|
代表field为成对的键值对
|
||||||
|
|
||||||
|
### Message Type Files Always have Field Presence
|
||||||
|
在proto3中,message-type field永远都存在field presence。故而,对于message-type field添加`optional`修饰符并不会改变该field的field presence。
|
||||||
|
|
||||||
|
例如,如下示例中定义的`Message2`和`Message3`对所有的语言都会生成相同的code,并且在binary json、text format格式下如下两种定义的数据展示都不会有任何区别
|
||||||
|
```proto
|
||||||
|
syntax="proto3";
|
||||||
|
|
||||||
|
package foo.bar;
|
||||||
|
|
||||||
|
message Message1 {}
|
||||||
|
|
||||||
|
message Message2 {
|
||||||
|
Message1 foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message3 {
|
||||||
|
optional Message1 bar = 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### well-formed messages
|
||||||
|
用`well-formed`来修饰`protobuf message`时,其代表被序列化或反序列化的bytes。在对bytes进行转化时,protoc parser将会校验是否`proto定义文件`是可转化的。
|
||||||
|
|
||||||
|
对于singular field,其可以在`wire-format` bytes中出现多次,parser会接收该输入,但是,在转化过程中,只有field的最后一次出现才有效。
|
||||||
|
|
||||||
|
### 在相同`.proto`中定义多个message type
|
||||||
|
可以在相同`.proto`文件中定义多个message type,示例如下所示:
|
||||||
|
```proto
|
||||||
|
message SearchRequest {
|
||||||
|
string query = 1;
|
||||||
|
int32 page_number = 2;
|
||||||
|
int32 results_per_page = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SearchResponse {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
> 应尽可能的在每个proto文件中包含较少的message type定义,在同一proto问文件中包含过多message type可能会造成依赖的膨胀。
|
||||||
|
|
||||||
|
### 删除Fields
|
||||||
|
当不再需要某个field,并且所有对该field的references都被从client code中移除时,可以从message type definition中移除该field。`但是,该field对应的field number必须被reserved,防止field number后续被重用`。
|
||||||
|
|
||||||
|
该field对应的field name也应该被reserved,以允许`按json或text-format进行编码`的消息你能偶被正常的转换。
|
||||||
|
|
||||||
|
#### reversed field number
|
||||||
|
在将field注释或删除时,将来使用者可能仍会对field number进行重用。为了避免该问题,可以将被删除field的field number添加到`reversed`列表中,示例如下:
|
||||||
|
```proto
|
||||||
|
message Foo {
|
||||||
|
reversed 2, 15, 9 to 11;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
> 上述示例中,`9 to 11`代表`9,10,11`
|
||||||
|
|
||||||
|
#### reversed field names
|
||||||
|
对被删除field的field name通常是安全的,除非使用TextProc或json的编码格式,在使用这些格式时field name也会参与序列化。为了避免该问题,可以将`deleted field name`添加到`reversed`列表中。
|
||||||
|
|
||||||
|
reversed names只会影响protoc compiler的行为,并不会对rumtime behavior造成影响,但是存在一个例外:
|
||||||
|
- 在parse过程中,TextProto实现会丢弃`reversed`中包含的未知fields,而不会抛出异常
|
||||||
|
- runtime json parse过程不会受到reversed names影响
|
||||||
|
|
||||||
|
使用reversed names示例如下:
|
||||||
|
```proto
|
||||||
|
message Foo {
|
||||||
|
reversed 2, 15, 9 to 11;
|
||||||
|
reversed "foo", "bar";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
> 上述示例中,将field numbers和field names分为了两个`reversed`语句,实际上,可以在同一行`reversed`语句中包含它们
|
||||||
|
|
||||||
|
### what generated from `.proto`
|
||||||
|
当针对`.proto`文件运行`protocol buffer compiler`时,compiler将会生成`所选中编程语言`对应的`和message type进行交互的代码`,生成的交互代码包括如下部分:
|
||||||
|
- getting and setting field values
|
||||||
|
- serialize message to outputstream
|
||||||
|
- parse message from input stream
|
||||||
|
|
||||||
|
对于常用变成语言,其生成文件内容如下:
|
||||||
|
- java:
|
||||||
|
- 对于java,其会为每个message type生成其对应的class
|
||||||
|
- 除了clas外,还会生成对应的Builder类,用于生成class实例
|
||||||
|
- go:
|
||||||
|
- 对于go,其会为每个message type生成`.pb.go`文件
|
||||||
|
|
||||||
|
### Scalar Value Types
|
||||||
|
一个scalar message field可以是如下类型
|
||||||
|
<table><tbody><tr><th>Proto Type</th><th>Notes</th></tr><tr><td>double</td><td></td></tr><tr><td>float</td><td></td></tr><tr><td>int32</td><td>Uses variable-length encoding. Inefficient for encoding negative
|
||||||
|
numbers – if your field is likely to have negative values, use sint32
|
||||||
|
instead.</td></tr><tr><td>int64</td><td>Uses variable-length encoding. Inefficient for encoding negative
|
||||||
|
numbers – if your field is likely to have negative values, use sint64
|
||||||
|
instead.</td></tr><tr><td>uint32</td><td>Uses variable-length encoding.</td></tr><tr><td>uint64</td><td>Uses variable-length encoding.</td></tr><tr><td>sint32</td><td>Uses variable-length encoding. Signed int value. These more
|
||||||
|
efficiently encode negative numbers than regular int32s.</td></tr><tr><td>sint64</td><td>Uses variable-length encoding. Signed int value. These more
|
||||||
|
efficiently encode negative numbers than regular int64s.</td></tr><tr><td>fixed32</td><td>Always four bytes. More efficient than uint32 if values are often
|
||||||
|
greater than 2<sup>28</sup>.</td></tr><tr><td>fixed64</td><td>Always eight bytes. More efficient than uint64 if values are often
|
||||||
|
greater than 2<sup>56</sup>.</td></tr><tr><td>sfixed32</td><td>Always four bytes.</td></tr><tr><td>sfixed64</td><td>Always eight bytes.</td></tr><tr><td>bool</td><td></td></tr><tr><td>string</td><td>A string must always contain UTF-8 encoded or 7-bit ASCII text, and cannot
|
||||||
|
be longer than 2<sup>32</sup>.</td></tr><tr><td>bytes</td><td>May contain any arbitrary sequence of bytes no longer than 2<sup>32</sup>.</td></tr></tbody></table>
|
||||||
|
|
||||||
|
上述scalar type,在各个编程语言中对应的类型如下所示:
|
||||||
|
<table style="width:100%;overflow-x:scroll"><tbody><tr><th>Proto Type</th><th>C++ Type</th><th>Java/Kotlin Type<sup>[1]</sup></th><th>Python Type<sup>[3]</sup></th><th>Go Type</th><th>Ruby Type</th><th>C# Type</th><th>PHP Type</th><th>Dart Type</th><th>Rust Type</th></tr><tr><td>double</td><td>double</td><td>double</td><td>float</td><td>float64</td><td>Float</td><td>double</td><td>float</td><td>double</td><td>f64</td></tr><tr><td>float</td><td>float</td><td>float</td><td>float</td><td>float32</td><td>Float</td><td>float</td><td>float</td><td>double</td><td>f32</td></tr><tr><td>int32</td><td>int32_t</td><td>int</td><td>int</td><td>int32</td><td>Fixnum or Bignum (as required)</td><td>int</td><td>integer</td><td>int</td><td>i32</td></tr><tr><td>int64</td><td>int64_t</td><td>long</td><td>int/long<sup>[4]</sup></td><td>int64</td><td>Bignum</td><td>long</td><td>integer/string<sup>[6]</sup></td><td>Int64</td><td>i64</td></tr><tr><td>uint32</td><td>uint32_t</td><td>int<sup>[2]</sup></td><td>int/long<sup>[4]</sup></td><td>uint32</td><td>Fixnum or Bignum (as required)</td><td>uint</td><td>integer</td><td>int</td><td>u32</td></tr><tr><td>uint64</td><td>uint64_t</td><td>long<sup>[2]</sup></td><td>int/long<sup>[4]</sup></td><td>uint64</td><td>Bignum</td><td>ulong</td><td>integer/string<sup>[6]</sup></td><td>Int64</td><td>u64</td></tr><tr><td>sint32</td><td>int32_t</td><td>int</td><td>int</td><td>int32</td><td>Fixnum or Bignum (as required)</td><td>int</td><td>integer</td><td>int</td><td>i32</td></tr><tr><td>sint64</td><td>int64_t</td><td>long</td><td>int/long<sup>[4]</sup></td><td>int64</td><td>Bignum</td><td>long</td><td>integer/string<sup>[6]</sup></td><td>Int64</td><td>i64</td></tr><tr><td>fixed32</td><td>uint32_t</td><td>int<sup>[2]</sup></td><td>int/long<sup>[4]</sup></td><td>uint32</td><td>Fixnum or Bignum (as required)</td><td>uint</td><td>integer</td><td>int</td><td>u32</td></tr><tr><td>fixed64</td><td>uint64_t</td><td>long<sup>[2]</sup></td><td>int/long<sup>[4]</sup></td><td>uint64</td><td>Bignum</td><td>ulong</td><td>integer/string<sup>[6]</sup></td><td>Int64</td><td>u64</td></tr><tr><td>sfixed32</td><td>int32_t</td><td>int</td><td>int</td><td>int32</td><td>Fixnum or Bignum (as required)</td><td>int</td><td>integer</td><td>int</td><td>i32</td></tr><tr><td>sfixed64</td><td>int64_t</td><td>long</td><td>int/long<sup>[4]</sup></td><td>int64</td><td>Bignum</td><td>long</td><td>integer/string<sup>[6]</sup></td><td>Int64</td><td>i64</td></tr><tr><td>bool</td><td>bool</td><td>boolean</td><td>bool</td><td>bool</td><td>TrueClass/FalseClass</td><td>bool</td><td>boolean</td><td>bool</td><td>bool</td></tr><tr><td>string</td><td>string</td><td>String</td><td>str/unicode<sup>[5]</sup></td><td>string</td><td>String (UTF-8)</td><td>string</td><td>string</td><td>String</td><td>ProtoString</td></tr><tr><td>bytes</td><td>string</td><td>ByteString</td><td>str (Python 2), bytes (Python 3)</td><td>[]byte</td><td>String (ASCII-8BIT)</td><td>ByteString</td><td>string</td><td>List<int></int></td><td>ProtoBytes</td></tr></tbody></table>
|
||||||
|
|
||||||
|
### default field values
|
||||||
|
当message执行反序列化操作时,如果encoded message bytes中并不包含指定的field,那么对反序列化后的对象,访问field时,将会返回一个默认值。
|
||||||
|
|
||||||
|
各种类型的默认值都不一样:
|
||||||
|
- 对于`string`类型,默认值为空字符串
|
||||||
|
- 对于`bytes`,默认值为empty bytes
|
||||||
|
- 对于`bool`类型,默认值为bool
|
||||||
|
- 对于numeric types,默认值为0
|
||||||
|
- 对于message fields,默认值为该field没有被设置
|
||||||
|
- 对于enum,默认值为`first defined enum value`
|
||||||
|
|
||||||
|
对于repeated fields,其默认值为`empty`(empty list)。
|
||||||
|
|
||||||
|
对于map fields,其默认值为emtpy(empty map)。
|
||||||
|
|
||||||
|
> #### implicit-presence
|
||||||
|
> 对于implicit presence scalar fields, 当消息被反序列化后,没有方法区分`该field是被显式设置为default value`还是`该field根本未被设置`。
|
||||||
|
|
||||||
|
### enumerations
|
||||||
|
在proto中定义枚举的示例如下:
|
||||||
|
```proto
|
||||||
|
enum Corpus {
|
||||||
|
CORPUS_UNSPECIFIED = 0;
|
||||||
|
CORPUS_UNIVERSAL = 1;
|
||||||
|
CORPUS_WEB = 2;
|
||||||
|
CORPUS_IMAGES = 3;
|
||||||
|
CORPUS_LOCAL = 4;
|
||||||
|
CORPUS_NEWS = 5;
|
||||||
|
CORPUS_PRODUCTS = 6;
|
||||||
|
CORPUS_VIDEO = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SearchRequest {
|
||||||
|
string query = 1;
|
||||||
|
int32 page_number = 2;
|
||||||
|
int32 results_per_page = 3;
|
||||||
|
Corpus corpus = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
在proto3中,`first value defined in enum`其值必须为0,并且,其名称必须为`{ENUM_TYPE_NAME}_UNSPECIFIED`或`{ENUM_TYPE_NAME}_UNKNOWN`。
|
||||||
|
|
||||||
|
#### enum value alias
|
||||||
|
当开启`allow_alias` option时,允许为相同的枚举值指定不同的枚举项。在反序列化时,所有的alias值都有效,但是只有第一个会被用于反序列化。
|
||||||
|
|
||||||
|
enum alias示例如下:
|
||||||
|
```proto
|
||||||
|
enum EnumAllowingAlias {
|
||||||
|
option allow_alias = true;
|
||||||
|
EAA_UNSPECIFIED = 0;
|
||||||
|
EAA_STARTED = 1;
|
||||||
|
EAA_RUNNING = 1;
|
||||||
|
EAA_FINISHED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EnumNotAllowingAlias {
|
||||||
|
ENAA_UNSPECIFIED = 0;
|
||||||
|
ENAA_STARTED = 1;
|
||||||
|
// ENAA_RUNNING = 1; // Uncommenting this line will cause a warning message.
|
||||||
|
ENAA_FINISHED = 2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
在使用枚举时,可以在一个message type中定义枚举,然后在另一个message type中使用枚举,语法如下` _MessageType_._EnumType_`。
|
||||||
|
|
||||||
|
### 修改message Type需要遵循的原则
|
||||||
|
如果旧的message type不再满足需求,想要添加新的field,且在修改message type后仍想和旧代码保持兼容,则必须要遵守如下原则
|
||||||
|
- 不要修改field number
|
||||||
|
- 在添加新field后,使用旧`meesage type`序列化到的消息格式仍然能被新的`message type`反序列化。同样的,新的`message type`序列化的消息同样能被旧的`message type`定义反序列化
|
||||||
|
- 即是,若`service A`和`service B`通过message type进行通信,`service A`为调用方而`service B`为被调用方,如果`service B`修改了message type定义,向message中添加了field,但是`serivce A`仍然使用的是旧的message定义,那么`service A`使用旧的message type定义序列化的数据,仍然能被`service B`反序列化
|
||||||
|
- 同样的,`service B`使用新的message type定义序列化的新消息数据仍然能被`service A`反序列化,`对于旧的消息定义,其会忽略新添加的字段`
|
||||||
|
- field可以被删除,但是被删除的field,其field number不能被重用。
|
||||||
|
- `int32, uint32, int64, uint64, bool`这些类型的值都是兼容的,代表可以将field的类型从一个修改为另一个,并且不会打破向前或向后兼容。
|
||||||
|
- `sint32和sint64`能够彼此兼容,但是和其他数值类型不相兼容
|
||||||
|
- `string和bytes`能够互相兼容,但要求bytes为有效的utf8字符串
|
||||||
|
- 嵌套的消息能够和bytes相互兼容,但是要求bytes内容为序列化后的message实例
|
||||||
|
- `fixed32和sfixed32`相互兼容,`fixed64`和`sfixed64`相互兼容
|
||||||
|
- 对于`string`, `bytes`,message fields, singular和`repeated`能够相互兼容。
|
||||||
|
- 假如被序列化的数据中包含repeated field,并且client期望该field为singluar,那么在反序列化时
|
||||||
|
- 若field为primitive type,client会取repeated field的最后一个值
|
||||||
|
- 如果field为message type,那么client会对所有的field element进行merge操作
|
||||||
|
- `enum`和`int32, uint32, int64, uint64`能够互相兼容
|
||||||
|
|
||||||
|
### Unknown Fields
|
||||||
|
如果被序列化的数据中包含parser无法识别的fields时,其被称为unknown fields。例如,old parser针对new sender发送的数据进行反序列化时,如果new sender发送的数据中包含new field,那么new
|
||||||
|
field即为unknown field。
|
||||||
|
|
||||||
|
proto3会对unknown fields进行保存,并在序列化和反序列化时包含它们,该行为和proto2一致。
|
||||||
|
|
||||||
|
#### unknown fields丢失
|
||||||
|
一些行为可能会造成unknown fields丢失,示例如下:
|
||||||
|
- 将消息序列化为json
|
||||||
|
- 遍历消息中的field并将其注入给新的message
|
||||||
|
|
||||||
|
为了避免unknown fields的丢失,遵循如下规则:
|
||||||
|
- 使用binary格式,在数据交换时避免使用text-format
|
||||||
|
- 使用message-oriented api来拷贝消息,例如`CopyFrom`或`MergeFrom`,不要使用field-by-field的拷贝方式
|
||||||
|
|
||||||
|
### Any
|
||||||
|
any的使用类似于泛型,允许在使用嵌套类型时无需声明其`.proto`定义,`Any`将会包含如下内容
|
||||||
|
- 任意被序列化为bytes的消息
|
||||||
|
- 一个唯一标识消息类型的url,用于对消息的反序列化
|
||||||
|
|
||||||
|
为了使用Any类型,需要`import google/protobuf/any.proto`,示例如下:
|
||||||
|
```proto
|
||||||
|
import "google/protobuf/any.proto";
|
||||||
|
|
||||||
|
message ErrorStatus {
|
||||||
|
string message = 1;
|
||||||
|
repeated google.protobuf.Any details = 2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
对于给定消息类型的默认type url为`type.googleapis.com/_packagename_._messagename_.`。
|
||||||
|
|
||||||
|
### OneOf
|
||||||
|
如果消息中包含多个singluar fields,并且在同一时刻最多只能有一个被设置,则可以使用OneOf特性。
|
||||||
|
|
||||||
|
OneOf fields类似于optional fields,但是所有oneof fields都保存在oneof共享内存中,在同一时间最多只能有一个field被设置。`对任一oneof member进行设置都会自动清空其他oneof members`。
|
||||||
|
|
||||||
|
可以通过`cause()或WhichoneOf()`方法来得知哪一个field被设置。
|
||||||
|
|
||||||
|
oneof使用如下所示:
|
||||||
|
```protobuf
|
||||||
|
message SampleMessage {
|
||||||
|
oneof test_oneof {
|
||||||
|
string name = 4;
|
||||||
|
SubMessage sub_message = 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
在使用oneof field时,可以向oneof中添加任意类型的field,除了`repeated`和`map`。
|
||||||
|
|
||||||
|
#### OneOf Feature
|
||||||
|
- 当parser对数据进行转换时,如果存在多个oneof member被设置,那么只有最后一个oneof member才会被使用
|
||||||
|
- oneof不能为repeated
|
||||||
|
- 反射api对oneof field也适用
|
||||||
|
|
||||||
|
#### 向后兼容问题
|
||||||
|
在向oneof中添加field时应该要注意,如果oneof的值返回`None/NOT_SET`,其可能代表`oneof没有被设置`或`其有可能被设置,但是设置的member不包含在旧版本的消息类型中`。
|
||||||
|
|
||||||
|
### Maps
|
||||||
|
如果想要创建一个map作为data definition的一部分,可以使用如下语法:
|
||||||
|
```proto
|
||||||
|
map<key_type, value_type> map_field = N;
|
||||||
|
```
|
||||||
|
上述示例中,`key_type`可以是任意整数类型或string类型。`value_type`可以是除了`map`外的任何类型。
|
||||||
|
|
||||||
|
创建示例如下所示;
|
||||||
|
```proto
|
||||||
|
map<string, Project> projects = 3;
|
||||||
|
```
|
||||||
|
#### map feature
|
||||||
|
- map fields 不能被`repeated`修饰
|
||||||
|
- map中值的遍历顺序以及值在format中的顺序是未定义的
|
||||||
|
- 在merge或反序列化过程中,如果存在多个相同的key,那么最后出现的key将会被使用
|
||||||
|
|
||||||
|
#### 向后兼容性
|
||||||
|
map synatx声明等价于如下声明:
|
||||||
|
```proto
|
||||||
|
message MapFieldEntry {
|
||||||
|
key_type key = 1;
|
||||||
|
value_type value = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
repeated MapFieldEntry map_field = N;
|
||||||
|
```
|
||||||
|
故而,不支持maps的protocol buffer实现也能够接收map数据。
|
||||||
|
|
||||||
|
### package
|
||||||
|
可以在`.proto`文件中指定一个package name,用于避免message type冲突,示例如下:
|
||||||
|
```proto
|
||||||
|
package foo.bar;
|
||||||
|
message Open { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
```proto
|
||||||
|
message Foo {
|
||||||
|
...
|
||||||
|
foo.bar.Open open = 1;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service In Rpc System
|
||||||
|
如果想要在rpc系统中使用message type类型,可以在proto文件中定义rpc service interface。protocol buffer compiler将会生成service interface code和stub,示例如下:
|
||||||
|
```proto
|
||||||
|
service SearchService {
|
||||||
|
rpc Search(SearchRequest) returns (SearchResponse);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### gRPC
|
||||||
|
与protocol buffer一起使用的最简单的rpc系统为`gRPC`。其是语言和平台中立的,其能够直接通过`.proto`文件产生rpc代码。
|
||||||
|
|
||||||
|
### json
|
||||||
|
`binary wire format`为protobuf的首选格式,但protobuf支持json编码规范。
|
||||||
|
|
||||||
|
### Options
|
||||||
|
`.proto`文件中的声明可以添加options。options并不会改变声明的含义,但是会影响特定上下文下对声明的处理。
|
||||||
|
|
||||||
|
#### 消息级别
|
||||||
|
- 部分option是文件级别的,其应当被写在文件作用域的最上方,而不应该在message type、enum、service之内
|
||||||
|
- 部分option则是message级别的,其应该被写在消息中
|
||||||
|
- 部分option是field级别的,其应该被写在field 定义之内
|
||||||
|
|
||||||
|
常用options如下
|
||||||
|
#### java_package (file option)
|
||||||
|
`java_package` option代表生成classes的package name。如果没有指定该选项,会默认使用`proto`文件的package。
|
||||||
|
|
||||||
|
使用示例如下:
|
||||||
|
```proto
|
||||||
|
option java_package = "com.example.foo";
|
||||||
|
```
|
||||||
|
|
||||||
|
#### java_outer_class_name (file option)
|
||||||
|
生成class文件的outer classname。如果该选项没有指定,默认为`proto`文件的文件名,使用示例如下:
|
||||||
|
```proto
|
||||||
|
option java_outer_classname = "Ponycopter";
|
||||||
|
```
|
||||||
|
|
||||||
|
#### java_multiple_files (file option)
|
||||||
|
如果该选项被指定为false,那么所有`.proto`文件产生的内容都会被嵌套在outer class中。
|
||||||
|
|
||||||
|
如果选项被指定为true,那么会为每个message type单独生成一个java文件。
|
||||||
|
|
||||||
|
该选项默认为false,使用示例如下:
|
||||||
|
```proto
|
||||||
|
option java_multiple_files = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### optimize_for (file option)
|
||||||
|
该选项可以被设置为`SPEED, CODE_SIZE, LIFE_RUNTIME`,其将会影响java和c++生成器:
|
||||||
|
- SPEED: SPEED为默认值,protocol buffer compiler将会生成序列化、反序列化、执行其他常用操作的代码。生成的代码是高度优化的
|
||||||
|
- CODE_SIZE: protocol buffer compiler将会生成最小的类代码,类代码中依赖共享、反射等操作来实现序列化、反序列化以及其他常用操作,指定该选项后生成的代码长度要比SPEED小得多,但是操作可能会更慢。
|
||||||
|
- LIFE_RUNTIME
|
||||||
|
|
||||||
|
使用示例如下所示:
|
||||||
|
```proto
|
||||||
|
option optimize_for = CODE_SIZE;
|
||||||
|
```
|
||||||
|
|
||||||
|
#### deprecated (field option)
|
||||||
|
如果该option为true,代表该field已经被废弃并不应该被新代码使用。在java中,其代表生成的代码中field将会被标注为`@Deprecated`。
|
||||||
|
|
||||||
|
使用示例如下所示:
|
||||||
|
```proto
|
||||||
|
int32 old_field = 6 [deprecated = true];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生成代码
|
||||||
|
在安装完protocol buffer compiler后,可以通过运行`protoc`命令来生成类文件,示例如下:
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
```
|
||||||
|
其中,IMPORT_PATH代表当使用import指令时,查找`.proto`文件的路径,如果省略,将会使用当前目录。如果要指定多个目录时,可以传递多个`--proto_path`选项。
|
||||||
|
|
||||||
|
在指定输入时,也可以指定多个proto文件。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user