github:https://github.com/protocolbuffers/protobuf
官方指南:https://developers.google.com/protocol-buffers/docs/overview
中文指南:
- Protobuf 2:https://colobu.com/2015/01/07/Protobuf-language-guide/
- Protobuf 3:https://colobu.com/2017/03/16/Protobuf3-language-guide/
Overview (是什么)
- Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准。
- 含有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。
- 用途:Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。适合通讯、数据存储等。
(1)Google用于 RPC 系统和持续数据存储系统。
(2)可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 - 目前提供了 C++、Java、Python 三种语言的 API。
Install
在github库 下载源码并编译。Ubuntu安装比较方便。
wget https://github.com/google/protobuf/releases/download/v2.4.1/protobuf-2.4.1.tar.gz
tar -xzf protobuf-2.4.1.tar.gz
cd protobuf-2.4.1
./configure
make
make check # 这一步会报错,忽略即可,也可以跳过这一步
make install
安装完成后会在 /usr/local/bin
目录下生成一个可执行文件 protoc
ls /usr/local/bin/protoc
protoc --version # 检查是否安装成功
在Ubuntu下一般会报错,protoc: error while loading shared libraries: libprotoc.so.8: cannot open shared
,原因是 protobuf 的默认安装路径是/usr/local/lib
,而/usr/local/lib
不在Ubuntu体系默认的 LD_LIBRARY_PATH
里,所以就找不到该lib
,解决方案:添加路径即可。
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
QuickStart
Step 1. demo简介
- 该程序(Protobuf & C++)由两部分组成。第一部分被称为 Writer,第二部分叫做 Reader。
- Writer 负责将一些结构化的数据写入一个磁盘文件;
- Reader 则负责从该磁盘文件中读取结构化数据并打印到屏幕上。
Step 2. 定义.proto文件
- 结构化数据,被称为Message,类似 java 或者 C 语言的数据定义
package lm; // package名字
message helloworld // 消息名称,该消息含有3个成员
{
required int32 id = 1; // ID
required string str = 2; // str
optional int32 opt = 3; //optional field, 可选成员,即消息中可以不包含该成员
}
Proto文件的命名规则packageName.MessageName.proto
Step 3. 编译.proto文件
// SRC_DIR:放置.proto的目录
// DST_DIR:输出.cc和.h文件的目录
protoc -I=$SRC_DIR --cpp_out=$DST_DIR
- 将生成两个文件
lm.helloworld.pb.h
: 定义了 C++ 类的头文件
lm.helloworld.pb.cc
: C++ 类的实现文件 - 在生成的头文件中,定义了一个 C++ 类 helloworld,后面的 Writer 和 Reader 将使用这个类来对消息进行操作。诸如对消息的成员进行赋值,将消息序列化等等都有相应的方法。
Step 4. 编写Writer 和 Reader
- 为什么需要protobuf,数据的Read和Write,按理说其实有很多方法,但Read的码农,必须清楚了解Write的码农所用的格式和细节,比如123可以按照int存储也可以按照string存储
- 使用protobuf的好处:
(1)需要处理的结构化数据由.proto文件描述,Writer只需要include proto生成的.h文件就可以使用固定格式的类了。
(2)protobuf集成了一系列全面鲁棒的方法来保证数据可以序列化和反序列化,可以放心的把数据存储交给protobuf - Writer代码
#include "lm.helloworld.pb.h"
int main(void) {
lm::helloworld msg1;
msg1.set_id(101); // 设置 id 的值
msg1.set_str(“hello”);
// Write the new address book back to disk.
fstream output("./log", ios::out | ios::trunc | ios::binary);
// SerializeToOstream 将对象序列化后写入一个 fstream 流
if (!msg1.SerializeToOstream(&output)) {
cerr << "Failed to write msg." << endl;
return -1;
}
return 0;
}
- Reader代码
#include "lm.helloworld.pb.h"
void ListMsg(const lm::helloworld & msg) {
cout << msg.id() << endl;
cout << msg.str() << endl;
}
int main(int argc, char* argv[]) {
// 声明类 helloworld 的对象 msg1
lm::helloworld msg1;
{
fstream input("./log", ios::in | ios::binary);
// 利用 ParseFromIstream 从一个 fstream 流中读取信息并反序列化
if (!msg1.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// ListMsg 中采用 get 方法读取消息的内部信息
ListMsg(msg1);
}
- 运行reader和writer结果如下:
>writer
>reader
101
Hello
- 把磁盘读写改为网络socket就可以扩展到RPC类的程序编写。
- 存储和交换正是 Protobuf 最有效的应用领域。
竞品对比
竞品:XML,JSON,Thrift
protobuf的优点:简单,快
-
项目 thrift-protobuf-compare
比较了这些类似的技术,图 1 显示了该项目的一项测试结果,Total Time.
Total Time 指一个对象操作的整个时间,包括创建对象,将对象序列化为内存中的字节序列,然后再反序列化的整个过程。从测试结果可以看到 Protobuf 的成绩很好,感兴趣的读者可以自行到网站
Protobuf 优点:
(1)Protobuf 有如 XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。修改简单。
(2)即“向后”兼容性好:人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。方便升级和迁移代码。
(3)Protobuf 语义更清晰,无需类似 XML 解析器的东西(因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据访问类以对 Protobuf 数据进行序列化、反序列化操作)。
(4)Protobuf 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例。Protobuf 缺点:
(1)相比较 XML,Protobuf功能简单,无法用来表示复杂的概念。
(2)XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。
(3)由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容。
推荐阅读:Protobuf更多高级应用和细节