Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,
目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。
他们用于 RPC 系统和持续数据存储系统。
Protocol Buffers 是一种轻便高效的结构化数据存储格式,
可以用于结构化数据串行化,或者说序列化。
它很适合做数据存储或 RPC 数据交换格式。
我们先来看看官方文档给出的定义和描述:
protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,
它可用于(数据)通信协议、数据存储等。
Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,
但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。
你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
简单来讲, ProtoBuf 是结构数据序列化[1] 方法,可简单类比于 XML[2],其具有以下特点:
序列化[1]:将结构数据或对象转换成能够被存储和传输(例如网络传输)的格式,
同时应当要保证这个序列化结果在之后(可能在另一个计算环境中)能够被重建回原来的结构数据或对象。
更为详尽的介绍可参阅 维基百科。
类比于 XML[2]:这里主要指在数据通信和数据存储应用场景中序列化方面的类比,
但个人认为 XML 作为一种扩展标记语言和 ProtoBuf 还是有着本质区别的。
ProtoBuf 可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
目前提供了 C++、Java、Python 三种语言的 API。
在分布式应用或者微服务架构中,各个服务之间通常使用json或者xml结构数据进行通信,
通常情况下,是没什么问题的,但是在高性能和大数据通信的系统当中,
如果有办法可以压缩数据量,提高传输效率,显然会给用户带来更快更流畅的体验。
对 ProtoBuf 的基本概念有了一定了解之后,我们来看看具体该如何使用 ProtoBuf。
2.1.1 操作命令
> git clone https://github.com/protocolbuffers/protobuf.git
> cd protobuf
> ./autogen.sh
> ./configure --prefix=/data/INSTALL_PATH
> make
> make install
2.1.2 错误提示及解决
1. 错误
> ./autogen.sh
+ mkdir -p third_party/googletest/m4
+ autoreconf -f -i -Wall,no-obsolete
./autogen.sh: line 37: autoreconf: command not found
解决:
> yum -y install autoconf automake libtool
2. 错误
> ./configure --prefix=/data/INSTALL_PATH/
checking whether g++ supports C++11 features with -h std=c++0x... no
configure: error: *** A compiler with support for C++11 language features is required.
解决:
换用高版本的带C++11的CentOS;
或:
升级gcc到高级版本《CentOS6 升级gcc版本以支持C++11》https://blog.csdn.net/luyumiao1990/article/details/104582968
2.2.1 操作命令
> cd examples
> make
> ./add_person_cpp add_person_cpp.data
按提示输入内容
> ./list_people_cpp add_person_cpp.data
Person ID: 1231231
Name: helloworld
Home phone #: 2637849234
Mobile phone #: 986789
Updated: 2020-03-03T15:50:51
2.2.2 错误提示及解决
1. 错误
> make
protoc $PROTO_PATH --cpp_out=. --java_out=. --python_out=. addressbook.proto
/bin/sh: protoc: command not found
make: *** [Makefile:28: protoc_middleman] Error 127
解决:
> vim /etc/profile
在最后添加:
export PATH=/data/INSTALL_PATH/bin:$PATH
保存并退出;
> source /etc/profile
2. 错误
> make
protoc $PROTO_PATH --cpp_out=. --java_out=. --python_out=. addressbook.proto
pkg-config --cflags protobuf # fails if protobuf is not installed
Package protobuf was not found in the pkg-config search path.
Perhaps you should add the directory containing `protobuf.pc'
to the PKG_CONFIG_PATH environment variable
Package 'protobuf', required by 'virtual:world', not found
make: *** [Makefile:43: add_person_cpp] Error 1
解决:
export PKG_CONFIG_PATH=/data/INSTALL_PATH/lib/pkgconfig
3. 错误
> make
pkg-config --cflags protobuf # fails if protobuf is not installed
-I/data/PJT-protobuf/PJT-google-protobuf/protobuf-install/include -pthread
c++ -std=c++11 add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
pkg-config --cflags protobuf # fails if protobuf is not installed
-I/data/PJT-protobuf/PJT-google-protobuf/protobuf-install/include -pthread
c++ -std=c++11 list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
javac -cp $CLASSPATH AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
/bin/sh: javac: command not found
make: *** [Makefile:67: javac_middleman] Error 127
解决:
> yum install -y java-1.8.0-openjdk-devel.x86_64
4.错误
> ./add_person_cpp -h
./add_person_cpp: error while loading shared libraries: libprotobuf.so.22: cannot open shared object file: No such file or directory
解决:
> vim /etc/profile
在最尾添加:
export LD_LIBRARY_PATH=/data/INSTALL_PATH/lib:$LD_LIBRARY_PATH
保存并退出;
> source /etc/profile
支持的语言及选择编译
示例程序支持多种语言,分别是:
# C++
cpp: add_person_cpp list_people_cpp
# DART: Dart是谷歌开发的计算机编程语言,它被用于web、服务器、移动应用 [2] 和物联网等领域的开发。
dart: add_person_dart list_people_dart
# Go: Go(又称 Golang)是 Google的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态强类型、编译型语言。
# Go 语言语法与 C 相近,但功能上有:内存安全,GC(垃圾回收),结构形态及 CSP-style 并发计算。
go: add_person_go list_people_go
gotest: add_person_gotest list_people_gotest
# Java
java: add_person_java list_people_java
# Python
python: add_person_python list_people_python
选择C++编译则是:
> make cpp
protoc $PROTO_PATH --cpp_out=. --java_out=. --python_out=. addressbook.proto
pkg-config --cflags protobuf # fails if protobuf is not installed
-I/data/PJT-protobuf/PJT-google-protobuf/protobuf-install/include -pthread
c++ -std=c++11 add_person.cc addressbook.pb.cc -o add_person_cpp `pkg-config --cflags --libs protobuf`
pkg-config --cflags protobuf # fails if protobuf is not installed
-I/data/PJT-protobuf/PJT-google-protobuf/protobuf-install/include -pthread
c++ -std=c++11 list_people.cc addressbook.pb.cc -o list_people_cpp `pkg-config --cflags --libs protobuf`
未编译前的文件:
addressbook.proto # PB消息的定义
add_person.cc # 接收输入并生成PB消息文件
list_people.cc # 读取PB消息文件并导致
Makefile # 编译文件
编译后新增文件:
addressbook.pb.h # 由 protoc生成的PB消息的头文件
addressbook.pb.cc # 由 protoc生成的PB消息的定义文件
add_person_cpp # 编译生成后的可执行程序
list_people_cpp # 编译生成后的可执行程序
> mkdir pb-test
> cd pb-test
> vim example_desc.proto
// [START declaration]
syntax = "proto3";
// [END declaration]
// [START messages]
message GslbDesc {
message EmbeddedMessage {
int32 int32Val = 1;
string stringVal = 2;
}
string stringVal = 1;
bytes bytesVal = 2;
EmbeddedMessage embeddedExample1 = 3;
repeated int32 repeatedInt32Val = 4;
repeated string repeatedStringVal= 5;
}
// [END messages]
【保存并退出】
>/data/INSTALL_PATH/bin/protoc --cpp_out=./ example_desc.proto
将生成头文件和函数定义文件,如下:
example_desc.pb.h
example_desc.ph.cc
> cat gslb_desc_test.cc
//
// Created by Hank on 2020-03-14.
//
#include
#include
#include
#include "gslb_desc.pb.h"
int main() {
GslbDesc gslb_desc;
gslb_desc.set_stringval("hello,world");
gslb_desc.set_bytesval("are you ok?");
GslbDesc_EmbeddedMessage *embeddedExample2 = new GslbDesc_EmbeddedMessage();
embeddedExample2->set_int32val(1);
embeddedExample2->set_stringval("embeddedInfo");
gslb_desc.set_allocated_embeddedexample1(embeddedExample2);
gslb_desc.add_repeatedint32val(2);
gslb_desc.add_repeatedint32val(3);
gslb_desc.add_repeatedstringval("repeated1");
gslb_desc.add_repeatedstringval("repeated2");
std::string filename = "gslb_desc_val_result";
std::fstream output(filename, std::ios::out | std::ios::trunc | std::ios::binary);
if (!gslb_desc.SerializeToOstream(&output)) {
std::cerr << "Failed to write gslb_desc." << std::endl;
exit(-1);
}
return 0;
}
编译与链接生成可执行程序:
> g++ -Wall -g -c gslb_desc.pb.cc -I../protobuf-install/include/ -L../protobuf-install/lib/ -lprotoc -lprotobuf -lprotobuf-lite
> g++ -Wall -g -c gslb_desc_test.cc -I../protobuf-install/include/ -L../protobuf-install/lib/ -lprotoc -lprotobuf -lprotobuf-lite
> g++ -Wall -g -o gslb_desc_test gslb_desc.pb.o gslb_desc_test.o -I../protobuf-install/include/ -L../protobuf-install/lib/ -lprotoc -lprotobuf -lprotobuf-lite
执行:
> ./gslb_desc_test
上面讲的都是protobuf的原理和基本示例。
在实际的生产工程中应用时,
为了使pb通信的各方都使用统一的pb定义,可以将pb的定义和生成单独提取出来,作为一个通用的框架工程。
由它来统一定义pb字段,并生成所有pb要用的.pb.cc 和 .pb.h,
然后再由需要使用pb的工程引用这些 .pb.cc 和 .pb.h 。
在项目管理上也需要有人统一收集,添加,管理pb字段及这个框架工程,
可以避免一些字段不一致,版本冲突等问题。
附:
六二 直方大,不习无不利。