Protocol Buffers使用指南

Protocol Buffers使用指南

一、简介

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

总结下,有以下特点

  • 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序

二、ProtoBuf相关概念

1、定义消息类型
syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
  • 文件的第一行指定您正在使用proto3语法:如果不这样做,则协议缓冲区编译器将假定您正在使用proto2。这必须是文件的第一行非空,非注释行。
  • 所述SearchRequest消息定义指定了三个字段(名称/值对),一个用于每条数据要在此类型的消息包括。每个字段都有一个名称和类型。

字段类型:

上述我们使用了两个整型和一个字符串,你也可以使用复合类型、枚举、或者其他消息类型

字段编号:

消息定义中的每个字段都有一个唯一的编号。这些字段号用于标识[消息二进制格式的]字段,一旦使用了消息类型,就不应更改这些字段号

字段规则:

消息字段可以是以下内容之一:

  • 单数:格式正确的邮件可以包含零个或一个此字段(但不能超过一个)。这是proto3语法的默认字段规则。
  • repeated:在格式正确的邮件中,此字段可以重复任意次(包括零次)。重复值的顺序将保留。
  • 在proto3中,repeated标量数字类型的字段packed默认情况下使用编码。

添加注释:

要将注释添加到.proto文件中,请使用//样式和/* ... */语法。

/* 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.
}
2、标量值类型

下表展示了.proto文件的类型和自动生成的文件的类型

.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[]byteString (ASCII-8BIT)ByteStringstringList
3、默认值

解析消息时,如果编码的消息不包含特定的单数元素,则已解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:

  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于布尔值,默认值为false。
  • 对于数字类型,默认值为零。
  • 对于[枚举],默认值为第一个定义的枚举值,必须为0。
  • 对于消息字段,未设置该字段。它的确切值取决于语言。

重复字段的默认值是空的(通常是使用适当语言的空列表)。

4、枚举

消息定义我们也可以使用枚举类型

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;
}
5、嵌套类型

我们也可以使用嵌套类型

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

如果要在消息类型之外重用此消息,则将其称为_Parent_._Type_

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}
6、Map

如果要创建关联映射作为数据定义的一部分,则协议缓冲区提供了方便的快捷方式语法:

map<key_type, value_type> map_field = N;

其中key_type可以是任何整数或字符串类型(不能为枚举),map 的value_type可以是任何类型的但不能为另一map(也就是说不支持嵌套)。

注意点:

  • map字段不能为repeated
  • map是无序的,你不能根据它的顺序来处理相关业务
  • 如果存在重复的键,则使用最后一个。如果
7、定义服务

如果要将消息类型与RPC(远程过程调用)系统一起使用,则可以在.proto文件中定义RPC服务接口,并且协议缓冲区编译器将以您选择的语言生成服务接口代码和存根。因此,例如,如果您想使用一种方法来定义RPC服务,该方法接受您的方法SearchRequest并返回SearchResponse,则可以在.proto文件中按以下方式定义它:

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

与协议缓冲区一起使用的最简单的RPC系统是gRPC:这是Google开发的与语言和平台无关的开源RPC系统。gRPC与协议缓冲区配合使用特别好,它使您可以.proto使用特殊的协议缓冲区编译器插件直接从文件中生成相关的RPC代码。

8、JSON对应

Proto3支持JSON中的规范编码,从而使在系统之间共享数据更加容易。下表中按类型对编码进行了描述。

如果JSON编码的数据中缺少某个值,或者如果该值为null,则在解析为协议缓冲区时,它将被解释为适当的[默认值]。

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

三、ProtoBuf使用

对 ProtoBuf 的基本概念有了一定了解之后,我们来看看具体该如何使用 ProtoBuf。 第一步,创建 .proto 文件,定义数据结构,如下例1所示:

// 例1: 在 xxx.proto 文件中定义 Example1 message
message Example1 {
    optional string stringVal = 1;
    optional bytes bytesVal = 2;
    message EmbeddedMessage {
        int32 int32Val = 1;
        string stringVal = 2;
    }
    optional EmbeddedMessage embeddedExample1 = 3;
    repeated int32 repeatedInt32Val = 4;
    repeated string repeatedStringVal = 5;
}
message xxx {
  // 字段规则:required -> 字段只能也必须出现 1 次
  // 字段规则:optional -> 字段可出现 0 次或1次
  // 字段规则:repeated -> 字段可出现任意多次(包括 0)
  // 类型:int32、int64、sint32、sint64、string、32-bit ....
  // 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)
  字段规则 类型 名称 = 字段编号;
}

在上例中,我们定义了:

  • 类型 string,名为 stringVal 的 optional 可选字段,字段编号为 1,此字段可出现 0 或 1 次
  • 类型 bytes,名为 bytesVal 的 optional 可选字段,字段编号为 2,此字段可出现 0 或 1 次
  • 类型 EmbeddedMessage(自定义的内嵌 message 类型),名为 embeddedExample1 的 optional 可选字段,字段编号为 3,此字段可出现 0 或 1 次
  • 类型 int32,名为 repeatedInt32Val 的 repeated 可重复字段,字段编号为 4,此字段可出现 任意多次(包括 0)
  • 类型 string,名为 repeatedStringVal 的 repeated 可重复字段,字段编号为 5,此字段可出现 任意多次(包括 0)

四、根据.proto文件生成代码

编译器命令如下:

protoc --proto_path=IMPORT_PATH --java_out=DST_DIR  path/to/file.proto
  • IMPORT_PATH指定.proto解析import指令时要在其中查找文件的目录。如果省略,则使用当前目录。可以通过--proto_path多次传递选项来指定多个导入目录。将按顺序搜索它们。-I=_IMPORT_PATH_可以用作的简写形式--proto_path
  • 您可以提供一个或多个输出指令:
    • --cpp_out在中生成C ++代码DST_DIR
    • --java_out在中生成Java代码DST_DIR
    • --kotlin_out在中生成其他Kotlin代码DST_DIR
    • --python_out在中生成Python代码DST_DIR
    • --go_out在中生成Go代码DST_DIR
    • --ruby_out在中生成Ruby代码DST_DIR。Ruby生成的代码参考即将推出!
    • --objc_out在中生成Objective-C代码DST_DIR。objective-c-generated)。
    • --csharp_out在中生成C#代码DST_DIR
    • --php_out在中生成PHP代码DST_DIR。为方便起见,如果DST_DIR结尾为.zip.jar,编译器会将输出写入具有给定名称的单个ZIP格式的存档文件。.jar根据Java JAR规范的要求,还将为输出提供清单文件。请注意,如果输出归档文件已经存在,它将被覆盖;编译器不够智能,无法将文件添加到现有存档中。
  • 您必须提供一个或多个.proto文件作为输入。.proto可以一次指定多个文件。尽管这些文件是相对于当前目录命名的,但是每个文件都必须位于IMPORT_PATHs之一中,以便编译器可以确定其规范名称。
// $SRC_DIR: .proto 所在的源目录文件夹
// --java_out: $DST_DIR: 生成的 java 代码的目标目录
// xxx.proto: 要针对哪个 proto 文件生成接口代码,如果生成全部把`xxx/proto/Member.proto`改为`xxx/proto/*.proto`

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/xxx.proto
// 如下示例
protoc -I=/Users/jarvan/IdeaProjects/workspace-jarvan/spring-boot-grpc/src/main/proto --java_out=/Users/jarvan/ /Users/jarvan/IdeaProjects/workspace-jarvan/spring-boot-grpc/src/main/proto/Member.proto
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值