Protocol Buffers
Protocol buffers,简称Protobuf,是一个独立于编程语言,独立于平台,且可拓展的自动序列化结构数据的机制。
本文档服务于想要在程序中使用Protobuf的JAVA,C++,Python开发者。概述中简单介绍了Protobuf,接下来将引导读者一步步开发自己的Protobuf程序。——包括更深一步的protocol buffer encoding。API reference documentation提供三种编程语言语法支持以及在书写.proto文件时的编程风格推荐。
开发者指导
什么是protocol buffers?
Protocol buffers提供了一种灵活、高效、自动序列化结构数据的机制,类比于 XML,但是它比 XML 更小、更快、更简单。仅需要自定义一次你所需的数据格式,然后用户就可以使用 Protobuf 编译器自动生成各种语言的源码,方便的读写用户自定义的格式化的数据。与语言无关,与平台无关,还可以在不破坏原数据格式的基础上,依据原有数据格式,更新现有的数据格式。
Protobuf如何工作
用户在 .proto 文件中定义 message 来指定所需要序列化的数据格式。每一个 message 都是一个小的信息逻辑单元,包含一系列的 name-value 值对。下面举例来说明一个简单的 .proto 文件,它定义了一条包含 Person 信息的 message。
message Person
{
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType
{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber
{
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
从以上代码来看,message 格式非常简单。每种类型的 message 包含一个或者多个唯一编码字段,每个字段由名称和值类型组成,值类型可以是数字(整形或者浮点型)、布尔值、字符串、原始字节,甚至是其他的 message(如上例所示)。Protobuf 允许 message 中包含 message,以达到分层嵌套。message 中可以定义 optional 字段、required 字段和 repeated 字段。
定义好 message 后,运行 Protobuf 编译器编译 .proto 文件,就可以生成存取数据的相关类。这些类包括简单的设置及读取字段(比如 name() 和 set_name() )的方法,也包括整个数据结构的 message 和原始字节之间的序列化/反序列化的转换方法。举个例子,如果你选择的是 C++ 语言,运行 Protobuf 编译器编译以上 .proto 文件生成 Person 类。你就能使用这个类去填充、序列化、检索 Person message。如下代码:
Person person;
person.set_name("zebra");
person.set_id(123);
person.set_email("zebra@example.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);
接下来,用如下代码来读取 message:
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail" << person.email() << endl;
Protobuf 是易于扩展的,可以向后兼容,我们可以在 message 中添加新字段,在解析的时候,老版本的数据就会忽略新增加的字段。因此,如果现有通讯协议使用 Protobuf 做为其数据格式,可以直接扩展该通讯协议,而不必担心着将会破坏现有的代码。关于使用 .proto 文件生成目标代码,后面将会介绍。
为什么不直接使用XML
Protocol buffers在序列化结构数据上比XML具有更多的优势:
- 更简单
- 小3-10倍
- 快20-100倍
- 歧义更少
- 生成的类更容易使用
举个栗子,如果你想存储一个人的信息,包括姓名和邮箱地址,下面是XML的写法:
<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>
而对应的protocol buffer message(使用protocol buffer 格式)如下:
# Textual representation of a protocol buffer.
# This is *not* the binary format used on the wire.
person {
name: "John Doe"
email: "jdoe@example.com"
}
当这个message被编码成protocol buffer二进制格式时(以上代码只是为了方便阅读和调试),只有大概28个字节长,100-200ns即可解析。而XML版本则最少需要69个字节,并且需要5,000-10,000ns解析时长。
同样的,操纵protocol buffer也非常简单:
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
在XML中可能需要如下的写法才行:
cout << "Name: "
<< person.getElementsByTagName("name")->item(0)->innerText()
<< endl;
cout << "E-mail: "
<< person.getElementsByTagName("email")->item(0)->innerText()
<< endl;
然而,protocol buffer也并非总是比XML好——比如说,protocol buffers并不适用于组建基于文本的文档标记,因为我们不能用简单的方法在文本中插入结构。此外,XML是易读性,和可编辑性更好的语言;protocol buffers的原始格式则不尽然。XML在某种程度上,是自解释的,而protocol buffers必须有消息定义(.proto文件)才能看出来是什么意思。
听起来很不错,哪怎么开始呢?
Download the package-包含了适用于Java,Python,和C++语言的源代码及编译器,以及I/O类和测试范例。根据README文件的指示,可以方便的安装编译器。
一旦上面的都做完了,请尝试跟着你所选择语言的入门指导完整的做一遍,将有助于你通过简单的例子来了解如何使用protocol buffers。
proto3介绍
最新版本是版本3,它有全新的语言版本:Protocol Buffers 3,和一些已经存在于proto2中的新特性。Proto3简化了protocol buffer语言,不仅易用性大大提升,而且支持更多编程语言:Java,C++,Python,Java Lite,Ruby,JavaScript,Objective-C,和C#。此外通过最新的Go protoc插件,还能生成用于Go语言的proto3代码,戳此github仓库golang/protobuf。更多的语言支持正在开发中。
目前我们只建议你在以下情况之一选择proto3:
- 你的项目所使用的语言是proto3刚刚支持的。
- 希望尝试使用我们最新的开源软件gRPC-强烈建议使用proto3来避免兼容性问题。
请注意,proto3和proto2的API并不是完全兼容的。为了避免兼容性问题,我们将在新的protocol buffers发行版本中支持先前的语言。
在当前的发行版本文档中可以了解proto3语法的主要变化,proto3的开发者文档即将出版!
(如果proto2和proto3的名字让你感到不解,这是因为我们当初开源protobuf时,它已经是谷歌的第二个版本,也被称为proto2。这也是为什么我们的开原版本号从v2.0.0开始的原因。)
历史漫谈
Protobuf最初是谷歌开发用来解决索引服务器的请求/响应协议的。在使用Protobuf之前,有一种格式用以处理请求&响应数据的编码和解码工作,并且支持多种版本的协议。这使得写出来的代码非常丑陋,比如:
if (version==3) {
...
}else if (version>4) {
if (version==5) {
...
}
...
}
通信协议因此变得越来越复杂,因为开发者必须确保请求者和接受者互相兼容,并且在一方开始使用新协议时,另外一方也要使用新的协议。
Protobuf可用于解决下类问题:
- 自动生成序列化与反序列化代码,避免人工处理编码问题。
- 除了用于短期RPC(远程过程调用)请求之外,开发者还使用该协议作为一种便捷的自描述格式,用于数据持久化。
- RPC服务器接口可以作为 .proto 文件来描述,而通过ProtocolBuffer的编译器生成存根(stub)类供用户实现服务器接口。
Protobuf现在已经是Google的混合语言数据标准了,现在已经正在使用的有超过48,162种报文格式定义和超过12,183个 .proto 文件。他们用于RPC系统和持续数据存储系统。