Protocol buffers
是一种灵活,高效,自动化的机制,用于序列化结构化的数据(如XML
),但是它更小,更快,更简单。可以定义数据如何被结构化,然后使用特定的生成的源代码轻松地将结构化数据在各种数据流中写入和读取,这支持各种编程语言。甚至可以更新数据结构,而不会破坏根据“旧”格式编译的已部署程序。
通过在.proto
文件中定义protocol buffers
消息类型来指定希望如何构建序列化信息。每个protocol buffers
消息都是一个小的逻辑信息记录,包含一系列名称-值对。以下是.proto
文件的一个非常基本的示例,该文件定义了包含有关人员信息的消息:
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;
}
如上所示,消息格式很简单:
protocol buffers
消息类型,允许分层次地构建数据可以指定:
可以在proto3指南
中找到有关编写.proto
文件的更多信息。
一旦定义了消息,就可以在.proto
文件上运行相应编程语言的protocol buffers
编译器来生成数据访问类,他们为每个字段提供了简单的访问器,如name()
和set_name()
,以及将整个结构序列化或解析为原始字节的方法。
例如,如果选择的语言是C++,在上面的例子上运行编译器将生成一个名为
Person
的类。然后,可以在应用程序中使用此类来填充,序列化和检索Person
的protocol buffers
消息。如下所示的代码。
Person person;
person.set_name("John Doe");
person.set_id(1234);
person.set_email("jdoe@example.com");
fstream output("myfile", ios::out | ios::binary);
person.SerializeToOstream(&output);
可以在以下位置阅读消息:
fstream input("myfile", ios::in | ios::binary);
Person person;
person.ParseFromIstream(&input);
cout << "Name: " << person.name() << endl;
cout << "E-mail: " << person.email() << endl;
可以在不破坏向后兼容性的情况下为邮件格式添加新字段,旧的二进制文件在解析时只是忽略新字段。因此,如果通信协议使用protocol buffers
作为数据格式,则可以扩展协议,而无需担心破坏现有代码。
将在API参考部分找到有关使用生成的protocol buffers
代码的完整参考,在protocol buffers
编码中找到有关protocol buffers
消息如何编码的更多信息。
对于序列化结构化数据,protocol buffers
比XML
具有许多优点:
例如,假设要为具有name
和email
的Person
建模。在XML中,需要:
<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>
而相应的protocol buffers
消息(protocol buffers
文本格式)是:
# protocol buffer的文本表示
# 这不是在实际传输中的二进制格式
person {
name: "John Doe"
email: "jdoe@example.com"
}
当此消息被编码为protocol buffers
二进制格式(上面的文本格式只是方便人类可读的表示形式,用于调试和编辑)时,它可能是28字节长并且需要大约100-200纳秒来解析。如果删除空格,XML
版本至少为69个字节,并且需要大约5000-10000纳秒才能解析。
此外,操作protocol buffers
要容易得多:
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 buffers
并不总是比XML
更好的解决方案。例如:
protocol buffers
不是使用标记对基于文本的文档(例如HTML
)建模的好方法,因为无法轻松地将结构与文本交错。XML
是人类可读和可编辑的; protocol buffers
在它原生的格式中不是人类可读和可编辑的。XML
在某种程度上也是自我描述的。只有拥有消息定义(如.proto文件
)时,protocol buffers
才有意义。下载这个包,它包含Java
,Python
和C++
版本的protocol buffers
编译器的完整源代码,以及I/O
和测试所需的类。要构建和安装编译器,请按照自述文件中的说明进行操作。
完成所有设置后,请尝试按照所选语言的教程进行操作,这将指导你创建一个使用protocol buffers
的简单应用程序。
proto3
简介最新的版本3发布了一个新的语言版本:Protocol Buffers语言版本3
(简称proto3
),以及现有语言版本(简称proto2
)中的一些新功能。Proto3
简化了protocol buffers
语言,既易于使用,又可以在更广泛的编程语言中使用:当前的版本允许使用Java
,C++
,Python
,Java Lite
,Ruby
,JavaScript
,Objective
和C#
来生成protocol buffers
代码。此外,可以使用最新的Go protoc
插件为Go生成proto3
代码,该插件可从golang/protobuf
Github存储库获得。更多的语言正在筹备中。
请注意,两种语言版本的API不完全兼容。为避免给现有用户带来不便,将继续在新protocol buffers
版本中支持以前的语言版本。
可以在发行说明中看到与当前默认版本的主要差异,并了解Proto3
语言指南中的proto3
语法。proto3的完整文档即将推出!
(如果名称proto2
和proto3
看起来有点令人困惑,那是因为最初开源protocol buffers
时,它实际上是Google的第二版语言,也称为proto2
,这也是开源版本号从v2开始的原因。
protocol buffers
最初是在Google开发的,用于处理索引服务器请求/响应协议。在protocol buffers
之前,有一种请求和响应的格式,它使用请求和响应的手动编组/解组,并支持许多版本的协议。 这导致一些非常丑陋的代码,如:
if (version == 3) {
...
} else if (version > 4) {
if (version == 5) {
...
}
...
}
明确格式化的协议也使新协议版本的推出变得复杂,因为开发人员必须确保请求的发起者和处理请求的实际服务器之间的所有服务器都能理解新协议,然后才能切换以开始使用新协议。
protocol buffers
开发用于解决这些问题:
但是,用户仍然需要自己手写解析代码。
随着系统的发展,它获得了许多其他功能和用途:
protocol buffers
作为一种方便的自描述格式用于持久存储数据(例如,在Bigtable
中)。protocol
文件的一部分,然后使用protocol
编译器生成stub
类,用户可以通过服务接口的实际实现来覆盖这些stub
类。现在protocol buffers
是Google的数据通用语言,在撰写本文时,Google代码树中定义了306,747种不同的消息类型,跨348,952个.proto
文件。它们既可用于RPC系统,也可用于各种存储系统中的数据持久存储。