ProtoBuf的使用和原理
一、簡介
Protobuf是一個靈活的、高效的用於序列化數據的協議。相比較XML和JSON格式,protobuf更小、更快、更便捷。Protobuf是跨語言的,並且自帶了一個編譯器(protoc),只需要用它進行編譯,可以編譯成Java、python、C++等代碼,然后就可以直接使用,不需要再寫其他代碼,自帶有解析的代碼。一條消息數據,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二進制序列化的10分之一。
二、安裝
1、下載代碼,https://github.com/google/protobuf
2、安裝protobuf
tar -xzf protobuf-2.1.0.tar.gz
cd protobuf
./configure --prefix=/usr/local/protobuf make make check make install
3、配置文件
1)vim /etc/profile 和 ~/.profile 中添加:
export PATH=$PATH:/usr/local/protobuf/bin/ export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/ 2)配置動態鏈接庫,vim /etc/ld.so.conf,在文件中添加/usr/local/protobuf/lib(注意: 在新行處添加) 3)執行:ldconfig
三、類似技術對比
1、優點
1)Protobuf同XML相比,主要優點在於性能高。
它以高效的二進制方式存儲,比XML小3到10倍,快20到100倍。
2)可以自定義數據結構,然后使用代碼生成器生成的代碼來讀寫這個數據結構。
你甚至可以在無需重新部署程序的情況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,即可利用各種不同語言或從各種不同數據流中對你的結構化數據輕松讀寫。
3)
“向后”兼容性好,用戶不必破壞已部署的、依靠“老”數據格式的程序就可以對數據結構進行升級。這樣程序就可以不必擔心因為消息結構的改變而造成的大規模的代碼重構或者遷移的問題。因為添加新的消息中的 field 並不會引起已經發布的程序的任何改變。
4)
Protobuf語義更清晰,無需類似XML解析器的東西。Protobuf 編譯器會將.proto文件編譯生成對應的數據訪問類以對Protobuf數據進行序列化、反序列化操作。
5)使用 Protobuf 無需學習復雜的文檔對象模型,
Protobuf 的編程模式比較友好,簡單易學,同時它擁有良好的文檔和示例,對於喜歡簡單事物的人們而言,Protobuf 比其他的技術更加有吸引力。
2、不足
1)Protbuf 與 XML 相比也有不足之處。它功能簡單,無法用來表示復雜的概念。
2)XML 已經成為多種行業標准的編寫工具,Protobuf 只是 Google 公司內部使用的工具,在通用性上還差很多。
3)由於文本並不適合用來描述數據結構,所以 Protobuf 也不適合用來對基於文本的標記文檔(如 HTML)建模。
4)由於 XML 具有某種程度上的自解釋性,它可以被人直接讀取編輯,在這一點上 Protobuf 不行,它以二進制的方式存儲,除非你有 .proto 定義,否則你沒法直接讀出 Protobuf 的任何內容。
3、舉例對比
protobuf和xml存入數據:
//在XML中建模Person的name和email字段: <person> <name>John Doe</name> <email>jdoe@example.com</email> </person> //ProtocolBuffer的文本表示: person { name: "John Doe" email: "jdoe@example.com" }
讀取數據:
//操作ProtocolBuffer也很簡單: 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() << end;
四、使用場景
1、需要和其它系統做消息交換的,對消息大小很敏感的,那么protobuf適合了,它語言無關,消息空間相對xml和json等節省很多。
2、小數據的場合。如果你是大數據,用它並不適合。
3、項目語言是c++,java,python的,因為它們可以使用google的源生類庫,序列化和反序列化的效率非常高。其他語言需要第三方或者自己寫,序列化和反序列化的效率不保證。
五、程序示例(C++版)
該程序示例的大致功能是,定義一個Persion結構體和存放Persion的AddressBook,然后一個寫程序向一個文件寫入該結構體信息,另一個程序從文件中讀出該信息並打印到輸出中。
1、address.proto文件
package tutorial; message Persion { required string name = 1; required int32 age = 2; } message AddressBook { repeated Persion persion = 1; }
編譯.proto文件,執行命令:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto,示例中執行命令protoc --cpp_out=/tmp addressbook.proto ,會在/tmp中生成文件addressbook.pb.h和addressbook.pb.cc。
2、write.cpp文件,向文件中寫入AddressBook信息,該文件是二進制的
#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; void PromptForAddress(tutorial::Persion *persion) { cout << "Enter persion name:" << endl; string name; cin >> name; persion->set_name(name); int age; cin >> age; persion->set_age(age); } int main(int argc, char **argv) { //GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOL_FILE" << endl; return -1; } tutorial::AddressBook address_book; { fstream input(argv[1], ios::in | ios::binary); if (!input) { cout << argv[1] << ": File not found. Creating a new file." << endl; } else if (!address_book.ParseFromIstream(&input)) { cerr << "Filed to parse address book." << endl; return -1; } } // Add an address PromptForAddress(address_book.add_persion()); { fstream output(argv[1], ios::out | ios::trunc | ios::binary); if (!address_book.SerializeToOstream(&output)) { cerr << "Failed to write address book." << endl; return -1; } } // Optional: Delete all global objects allocated by libprotobuf. //google::protobuf::ShutdownProtobufLibrary(); return 0; }
編譯write.cpp文件,執行命令:
g++ addressbook.pb.cc write.cpp -o write `pkg-config --cflags --libs protobuf` ,注意,這里的`符號在鍵盤數字1鍵左邊,也就是和~是同一個按鍵。
3、read.cpp文件,從文件中讀出AddressBook信息並打印
#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; void ListPeople(const tutorial::AddressBook& address_book) { for (int i = 0; i < address_book.persion_size(); i++) { const tutorial::Persion& persion = address_book.persion(i); cout << persion.name() << " " << persion.age() << endl; } } int main(int argc, char **argv) { //GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOL_FILE" << endl; return -1; } tutorial::AddressBook address_book; { fstream input(argv[1], ios::in | ios::binary); if (!address_book.ParseFromIstream(&input)) { cerr << "Filed to parse address book." << endl; return -1; } input.close(); } ListPeople(address_book); // Optional: Delete all global objects allocated by libprotobuf. //google::protobuf::ShutdownProtobufLibrary(); return 0; }
編譯read.cpp文件,
g++ addressbook.pb.cc read.cpp -o read `pkg-config --cflags --libs protobuf`
4、執行程序結果
ref:
http://www.cnblogs.com/luoxn28/p/5303517.html
http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html#resources
