Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南

Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南

约定:为方便书写,ProtocolBuffers在下文中将已Protobuf代替。

本指南将向您描述如何使用protobuf定义i结构化Protobuf数据,包括.proto文件语法和如何使用.proto文件生成数据存取类。

作为一个参考指南,本文档将以示例的形式一步步向您介绍Protobuf的特点。您可以参考您所选择的语言的示例。tutorial

--------------------------------------小小的分割线-----------------------------------------

定义一个消息类型

首先,看一个非常简单的例子,比如说你想定义一个 搜索请求消息 ,每个搜索请求都有一个 查询的字符串(关键字:比如我们上百度搜索 《报告老板》),和我们搜索出来的一个感兴趣的网页,以及搜索到的所有网页总数。 来看看这个.proto文件是如何定义的。

1 message SearchRequest {
2   required string query = 1;
3   optional int32 page_number = 2;
4   optional int32 result_per_page = 3;
5 }

这个"搜索请求"消息指定了三个字段(名称/属性 组合),每一个你想要包含在这类型的信息内的东西,都必须有一个字段,每个字段有一个名称和类型!

指定字段类型

在上面的示例中,所有的字段都是标量类型(scalar types):两个整数(integers:page_number 和 result_per_page)和一个字符串(string:query:查询的关键字),不过你可以在你的字段内指定符合类型。包括枚举类型(enumerations)和其他的消息类型

分配指定标签号

如你所见,每个消息的字段都有一个唯一的数字标签,这些标签用来表示你的字段在二进制消息(message binary format)中处的位置。并且一旦指定标签号,在使用过程中是不可以更改的,标记这些标签号在1-15的范围内每个字段需要使用1个字节用来编码这一个字节包括字段所在的位置和字段的类型!(需要更多关于编码的信息请点击Protocol Buffer Encoding)。标签号在16-2047需要使用2个字节来编码。所以你最好将1-15的标签号为频繁使用到的字段所保留。如果将来可能会添加一些频繁使用到的元素,记得留下一些1-15标签号。

最小可指定的标签号为1,最大的标签号为229 - 1或者536870911。不能使用19000-19999的标签号(FieldDescriptor::kFirstReservedNumber 至 FieldDescriptor::kLastReservedNumber) 这些标签号是为protobuf内部实现所保留的,如果你在.proto文件内使用了这些标签号Protobuf编译器将会报错!

指定字段规则

消息字段可以被指定为以下三种:

  • required: 完整的消息内必须拥有此字段。此字段是必须拥有的 (双方都要有)
  • optional: 完整的消息内此字段是可选的,可拥有也可以没有(双方可选)
  • repeated: 完整的消息内本字段的值可以拥有任意个,重复的值的次数会保存下来。(双方可选,数组)

因为历史的原因:repeated字段如果是基本的数字类型的话会无法编码。新的代码应该使用特殊的关键字[packed=true] 来使其得到有效的编码.例如

[cpp] view plaincopyprint?

  1. repeated int32 samples = 4 [packed=true];

[cpp] view plaincopyprint?

  1. repeated int32 samples = 4 [packed=true];

注意:你应该小心将字段设置为required,如果你希望在某些情况下取消required字段的读写,它将改变字段为optional属性,旧的的读取方将会认为此消息不完全。可能会无意的将其丢弃。你应该考虑自定义一个消息检查程序。google的一些工程师认为使用optinal字段的好处大于required。但是显然这个观点并不是通用的。

添加更多的消息类型

多个消息类型可以定义在同一个.proto文件内,这对定义多个有关联的消息是是十分有用的。例如,如果你想定义一个用于回复SearchResponse消息,你可以像这样在.proto内添加。

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;
  4. optional int32 result_per_page = 3;
  5. }
  6. message SearchResponse {
  7. ...
  8. }

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;
  4. optional int32 result_per_page = 3;
  5. }
  6. message SearchResponse {
  7. ...
  8. }

添加注释

添加注释的方式和C/C++是一样的。使用//

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;// Which page number do we want?
  4. optional int32 result_per_page = 3;// Number of results to return per page.
  5. }

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;// Which page number do we want?
  4. optional int32 result_per_page = 3;// Number of results to return per page.
  5. }

.proto文件会生成什么?

当你使用protobuf编译器编译一个.proto文件,它会生成在.proto内你描述的消息类型的操作代码,这些代码是根据你所选择的编程功能语言决定的。这些操作代码内包含了设置字段值 和读取字段值,以及序列化到输出流 和 从输入流反序列化。

C++:编译器会按照每个.proto文件生成与其对应的.h和.cc文件,每个消息类似都有独立的消息操作类。

Java:编译器将会生成一个.java文件和一个操作类,此操作类为所有消息类型所共有, 使用一个特别的Builder类为每个消息类型实例化.

Python:有一点不同 – 编译器会为每个消息生成一个模块每个模块有一个静态描述符, 该模块与一个元类在运行时创建一个需要的数据操作类。

从你所选择语言的例程,你可以找到更多关于API的内容, 需要关于API的详细信息, 参考: API reference.

标量值类型

一个消息的字段如果要使用标量可使之为以下类型 –这个表格显示了在.proto文件内可以指定的类型, 与自动生成的相对类型!

.proto Type Notes C++ Type Java Type Python Type[2]
double   double double float
float   float float float
int32 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int int
int64 Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long int/long[3]
uint32 Uses variable-length encoding. uint32 int[1] int/long[3]
uint64 Uses variable-length encoding. uint64 long[1] int/long[3]
sint32 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int int
sint64 Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. int64 long int/long[3]
fixed32 Always four bytes. More efficient than uint32 if values are often greater than 228. uint32 int[1] int
fixed64 Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long[1] int/long[3]
sfixed32 Always four bytes. int32 int int
sfixed64 Always eight bytes. int64 long int/long[3]
bool   bool boolean boolean
string A string must always contain UTF-8 encoded or 7-bit ASCII text. string String str/unicode[4]
bytes May contain any arbitrary sequence of bytes. string ByteString str

你可以在Protocol Buffer Encoding.找到更多关于.这些类型如何编码,如何序列化定义消息的信息!

[1] 在Java中, 无符号32位和64位整数与其有符号相对应, 最高位用来保存符号!

[2] 在所有情况下, 设置某个字段的值将会执行类型检查确保其值是合法的!

[3] 64位或32位无符号整数在解码中会以long来解码, 给字段赋值的时候可以是int.但是在所有情况下,赋值的时候会转变为其目标类型 . 详见 [2].

[4] Python的字符串在解码时候会以unicode来描述,但是同样的可以给其赋值为ascii字符串  (此乃弦外之音).

Optional 字段与其默认值

如上所述,在描述一个消息的时候可以用optional指定字段约束,一个消息可以包含也可以不包含optional元素。当一个消息被解析,如果其没有一个optional字段,被解析的消息对象就会将其相对的字段设置为其字段的默认值。这个默认值可以在描述消息的时候被指定。例如。比如你想设置SearchRequest的 result_per_page的默认值为10.

[cpp] view plaincopyprint?

  1. optional int32 result_per_page = 3 [default = 10];

[cpp] view plaincopyprint?

  1. optional int32 result_per_page = 3 [default = 10];

如果一个optional字段没有被指定其默认值。其默认值被自动替换为:

1.字符串:为空字符串.

2.bool:为false.

3.数字类型:为0;

4.枚举值:为第一个枚举值

枚举值

当你定义消息格式的时候, 也许你希望其中的一个字段的的值为一个预定义的值类表中的一个. 比方说, 在SearchRequest消息中你想定义一个 corpus 字段, corpus字段的值可以为:" UNIVERSALWEBIMAGESLOCALNEWSPRODUCTS 或者 VIDEO". 你可以非常简单的给你的消息添加一个枚举类型 - 一个枚举字段类型其值指定被指定为一个常量的集合 (如果你尝试赋值一个不一样的值, 解析器将会认为这个字段为未知字段). 在下面的例子中 我们给corpus字段指定为枚举类型与其可能的值 :

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;
  4. optional int32 result_per_page = 3 [default = 10];
  5. enum Corpus {
  6. UNIVERSAL = 0;
  7. WEB = 1;
  8. IMAGES = 2;
  9. LOCAL = 3;
  10. NEWS = 4;
  11. PRODUCTS = 5;
  12. VIDEO = 6;
  13. }
  14. optional Corpus corpus = 4 [default = UNIVERSAL];
  15. }

[cpp] view plaincopyprint?

  1. message SearchRequest {
  2. required string query = 1;
  3. optional int32 page_number = 2;
  4. optional int32 result_per_page = 3 [default = 10];
  5. enum Corpus {
  6. UNIVERSAL = 0;
  7. WEB = 1;
  8. IMAGES = 2;
  9. LOCAL = 3;
  10. NEWS = 4;
  11. PRODUCTS = 5;
  12. VIDEO = 6;
  13. }
  14. optional Corpus corpus = 4 [default = UNIVERSAL];
  15. }

你可以为一个枚举常量定义别名,如果你需要这样做的话需要将allow_alias设置为true。否则如果出现别名的话编译器将会报错! 

[cpp] view plaincopyprint?

  1. enum EnumAllowingAlias {
  2. option allow_alias = true;
  3. UNKNOWN = 0;
  4. STARTED = 1;
  5. RUNNING = 1;
  6. }
  7. enum EnumNotAllowingAlias {
  8. UNKNOWN = 0;
  9. STARTED = 1;
  10. // RUNNING = 1;  //不注释这行的话会引发一个错误异常

[cpp] view plaincopyprint?

  1. enum EnumAllowingAlias {
  2. option allow_alias = true;
  3. UNKNOWN = 0;
  4. STARTED = 1;
  5. RUNNING = 1;
  6. }
  7. enum EnumNotAllowingAlias {
  8. UNKNOWN = 0;
  9. STARTED = 1;
  10. // RUNNING = 1;  //不注释这行的话会引发一个错误异常

枚举值的范围必须在32位整数之内.枚举值的编码使用可变长度的整数,负数会非常低效所以,不推荐使用。你可以在一个消息内部定义一个枚举类型,比如上面的例子。或者也可以在消息的外部定义。这些枚举类型是可以在.proto文件内中重用的,你可以在消息内定义个枚举类型。然后在不同的消息类型中使用它!可以使用 MessageType.EnumType来访问。当你运行编译器编译.proto文件中的枚举类型时,生成的代码会有一个相对应的枚举值(JAVA 或者C++),或者有一个特别的EnumDescriptor类(python)用于在运行时生成一个符号常量集合。

更多关于枚举类型的信息查询 generated code guide 选择你使用的语言。

待续....................

时间: 2024-08-02 06:58:05

Protocol Buffers(Protobuf) 官方文档--Protobuf语言指南的相关文章

Protocol Buffers官方文档(proto3语言指南)

本文是对官方文档的翻译,大部分内容都是引用其他一些作者的优质翻译使文章内容更加通俗易懂(自己是直译,读起来有点绕口难理解,本人英文水平有限),参考的文章链接在文章末尾 这篇指南描述如何使用protocol buffer语言来组织你的protocol buffer数据,包括.proto文件的语法规则以及如何通过.proto文件来生成数据访问类代码. Defining A Message Type(定义一个消息类型) syntax = "proto3"; message SearchReq

官方文档 恢复备份指南六 Configuring the RMAN Environment: Advanced Topics

RMAN高级设置. 本章内容: Configuring Advanced Channel Options  高级通道选项 Configuring Advanced Backup Options 高级备份选项 Configuring Auxiliary Instance Data File Names 配置辅助实例文件名 Configuring the Snapshot Control File Location 配置控制文件快照 Configuring RMAN for Use with a S

官方文档 恢复备份指南四 Starting and Interacting with the RMAN Client

本章讲: Starting and Exiting RMAN Specifying the Location of RMAN Output                                                        指定RMAN输出位置 Setting Globalization Support Environment Variables for RMAN            设置RMAN全局变量 Entering RMAN Commands         

官方文档 恢复备份指南三 Recovery Manager Architecture

本节讨论以下问题: About the RMAN Environment                        关于RMAN环境 RMAN Command-Line Client                            RMAN命令行 RMAN Channels                                                      RMAN通道 RMAN Repository                                

官方文档 恢复备份指南七 Using Flashback Database and Restore Points

本章内容: Understanding Flashback Database, Restore Points and Guaranteed Restore Points Logging for Flashback Database and Guaranteed Restore Points Prerequisites for Flashback Database and Guaranteed Restore Points Using Normal and Guaranteed Restore P

官方文档 恢复备份指南二 Getting Started with RMAN

本章对RMAN进行基本的熟悉和了解 1.Overview of the RMAN Environment RMAN运行时需要的最小环境: target database          :RMAN用target命令连接的数据库,在该数据库上执行备份和恢复操作,并备份信息(RMAN reposity 资源库)记录在target数据库的控制文件中. RMAN client              :rman客户端,即RMAN可执行程序.在$ORACLE_HOME/bin下 RMAN运行时可能需要

官方文档 恢复备份指南五 Configuring the RMAN Environment

本章内容: Configuring the Environment for RMAN Backups    配置RMAN环境 Configuring RMAN to Make Backups to a Media Manager 备份到介质管理器 Configuring the Fast Recovery Area    配置快速恢复区 Configuring the Backup Retention Policy    配置备份保留策略 Backup Optimization and the

官方文档 恢复备份指南八 RMAN Backup Concepts

本章内容 Consistent and Inconsistent RMAN Backups Online Backups and Backup Mode Backup Sets Image Copies Multiple Copies of RMAN Backups Control File and Server Parameter File Autobackups Incremental Backups Backup Retention Policies 1.Consistent and In

Protocol Buffers官方文档(开发指南)

本文是对官方文档的翻译,然后截取了一篇非常优秀的文章片段来帮助理解,本人英文水平有限,基本都是直译,如果有不理解的地方请参考英文官方文档,参考的文章链接在文章末尾 protocol buffers简介 protocol buffer是google的一个开源项目,它是用于结构化数据串行化的灵活.高效.自动的方法,例如XML,不过它比xml更小.更快.也更简单.你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构.你甚至可以在无需重新部署程序的情况下更新数据结构 protocol