大數據 --> ProtoBuf的使用和原理

ProtoBuf的使用和原理

1、簡介

  Protobuf是一個靈活的、高效的用於序列化數據的協議。相比較XML和JSON格式,protobuf更小、更快、更便捷。Protobuf是跨語言的,而且自帶了一個編譯器(protoc),只須要用它進行編譯,能夠編譯成Java、python、C++等代碼,而後就能夠直接使用,不須要再寫其餘代碼,自帶有解析的代碼。一條消息數據,用protobuf序列化後的大小是json的10分之一,xml格式的20分之一,是二進制序列化的10分之一。
 

2、安裝

一、下載代碼,https://github.com/google/protobuf
二、安裝protobuf
tar -xzf protobuf-2.1.0.tar.gz 
cd protobuf
./configure --prefix=/usr/local/protobuf make make check make install

三、配置文件html

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

 

3、相似技術對比

一、優勢

  1)Protobuf同XML相比,主要優勢在於性能高。 它以高效的二進制方式存儲,比XML小3到10倍,快20到100倍。
  2)能夠自定義數據結構,而後使用代碼生成器生成的代碼來讀寫這個數據結構。 你甚至能夠在無需從新部署程序的狀況下更新數據結構。只需使用 Protobuf 對數據結構進行一次描述,便可利用各類不一樣語言或從各類不一樣數據流中對你的結構化數據輕鬆讀寫。
  3) 「向後」兼容性好,用戶沒必要破壞已部署的、依靠「老」數據格式的程序就能夠對數據結構進行升級。這樣程序就能夠沒必要擔憂由於消息結構的改變而形成的大規模的代碼重構或者遷移的問題。由於添加新的消息中的 field 並不會引發已經發布的程序的任何改變。
  4) Protobuf語義更清晰,無需相似XML解析器的東西。Protobuf 編譯器會將.proto文件編譯生成對應的數據訪問類以對Protobuf數據進行序列化、反序列化操做。
  5)使用 Protobuf 無需學習複雜的文檔對象模型, Protobuf 的編程模式比較友好,簡單易學,同時它擁有良好的文檔和示例,對於喜歡簡單事物的人們而言,Protobuf 比其餘的技術更加有吸引力。
 

二、不足

  1)Protbuf 與 XML 相比也有不足之處。它功能簡單,沒法用來表示複雜的概念。
  2)XML 已經成爲多種行業標準的編寫工具,Protobuf 只是 Google 公司內部使用的工具,在通用性上還差不少。
  3)因爲文本並不適合用來描述數據結構,因此 Protobuf 也不適合用來對基於文本的標記文檔(如 HTML)建模。
  4)因爲 XML 具備某種程度上的自解釋性,它能夠被人直接讀取編輯,在這一點上 Protobuf 不行,它以二進制的方式存儲,除非你有 .proto 定義,不然你無法直接讀出 Protobuf 的任何內容。
 

三、舉例對比

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"
}

讀取數據:java

//操做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;

 

4、使用場景

  一、須要和其它系統作消息交換的,對消息大小很敏感的,那麼protobuf適合了,它語言無關,消息空間相對xml和json等節省不少。
  二、小數據的場合。若是你是大數據,用它並不適合。
  三、項目語言是c++,java,python的,由於它們可使用google的源生類庫,序列化和反序列化的效率很是高。其餘語言須要第三方或者本身寫,序列化和反序列化的效率不保證。
 
 

5、程序示例(C++版)

     該程序示例的大體功能是,定義一個Persion結構體和存放Persion的AddressBook,而後一個寫程序向一個文件寫入該結構體信息,另外一個程序從文件中讀出該信息並打印到輸出中。
一、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。
 
二、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鍵左邊,也就是和~是同一個按鍵。
 
三、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`
 
四、執行程序結果
 
 
ref:
http://www.cnblogs.com/luoxn28/p/5303517.html
http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html#resources
相關文章
相關標籤/搜索