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] 来使其得到有效的编码.例如

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

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

添加更多的消息类型

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

  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. }
  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++是一样的。使用//

  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. }
  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.

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

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

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

2.bool:为false.

3.数字类型:为0;

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

枚举值

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

  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. }
  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。否则如果出现别名的话编译器将会报错! 

  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;  //不注释这行的话会引发一个错误异常
  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 选择你使用的语言。

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

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

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

    本文是对官方文档的翻译,大部分内容都是引用其他一些作者的优质翻译使文章内容更加通俗易懂(自己是直译,读起来有点绕口难理解,本人英文水平有限),参考的文章链接在文章末尾 这篇指南描述如何使用protoc ...

  2. 《KAFKA官方文档》入门指南(转)

    1.入门指南 1.1简介 Apache的Kafka™是一个分布式流平台(a distributed streaming platform).这到底意味着什么? 我们认为,一个流处理平台应该具有三个关键 ...

  3. 官方文档 恢复备份指南一 Introduction to Backup and Recovery

    1.备份分为:物理备份和逻辑备份    物理备份:备份数据文件  控制文件  归档日志文件     逻辑备份:EXP EXPDP备份等 物理备份为主,逻辑做补充     2.错误的类型         ...

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

    RMAN高级设置. 本章内容: Configuring Advanced Channel Options  高级通道选项 Configuring Advanced Backup Options 高级备 ...

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

    本节讨论以下问题: About the RMAN Environment                        关于RMAN环境 RMAN Command-Line Client        ...

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

    本章内容 Consistent and Inconsistent RMAN Backups Online Backups and Backup Mode Backup Sets Image Copie ...

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

    本章讲: Starting and Exiting RMAN Specifying the Location of RMAN Output                                ...

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

    本章对RMAN进行基本的熟悉和了解   1.Overview of the RMAN Environment    RMAN运行时需要的最小环境:      target database       ...

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

    本章内容: Understanding Flashback Database, Restore Points and Guaranteed Restore Points Logging for Fla ...

随机推荐

  1. 工作流模式与K2实现- (1)

    背景 工作流产品众多,而它们之间又缺乏统一的标准,使得不同的产品之间很难实现协同工作.为了解决这一问题,工作流管理联盟(WFMC)于1993 年成立,并提出了工作流参考模型,制定了五个标准接口. 其中 ...

  2. [转]redis.conf的配置解析

    # redis 配置文件示例 # 当你需要为某个配置项指定内存大小的时候,必须要带上单位, # 通常的格式就是 1k 5gb 4m 等酱紫: # # 1k => 1000 bytes # 1kb ...

  3. C语言 生成随机数

    #include<stdio.h> #include<time.h> #include<Windows.h> void main1(){ //定义一个时间类型 ti ...

  4. 从客户端中检测到有潜在危险的request.form值

    今天被这个问题卡住了,在用到CKEDITOR的时候,老是报错显示输入字符存在潜在危险,之后百度了一下,试了这两种方法: 解决方案一:    在.aspx文件头中加入这句:    <%@ Page ...

  5. JS案例之6——瀑布流布局(1)

    在实际的项目中,偶尔会用到一种布局——瀑布流布局.瀑布流布局的特点是,在多列布局时,可以保证内容区块在水平方向上不产生大的空隙,类似瀑布的效果.简单的说,在垂直列表里,内容区块是一个挨着一个的.当内容 ...

  6. 20145208 《Java程序设计》第6周学习总结

    20145208 <Java程序设计>第6周学习总结 教材学习内容总结 输入与输出 InputStream与OutputStream 从应用程序角度来看,如果要将数据从来源取出,可以使用输 ...

  7. typeof和instanceof简介及用法

    typeof 使用方式:typeof a 或者 typeof (a) 返回一个string类型的值 顾名思义,这货是检查类型的,输出的是一个string值,直接看下面的检测代码: console.lo ...

  8. 人家为撩妹就鼓捣个网页,我做了个约炮APP(已开源)

    每年初夏第一场雷雨刚过,漫步河边的草坪,总是能闻到伴随着泥土的清新,这不是coco的前香,让人神魂颠倒:也不是gucci的后香,让人痴迷如梦.如24节气一样,它提醒人们,夏天到了.昨晚成都下了第一场雷 ...

  9. Git.Framework 框架随手记--IIS7运行序列化问题

    客户反馈系统又登录不了,这是最近几次连续出现相同的问题,从日志反应情况来看: 日志级别:[info] 日志位置:Git.Framework.Resource.ResourceManager 日志时间: ...

  10. C# HttpWebRequest提交数据方式浅析

    C# HttpWebRequest提交数据方式学习之前我们先来看看什么是HttpWebRequest,它是 .net 基类库中的一个类,在命名空间 System.Net 下面,用来使用户通过HTTP协 ...