本次分上下兩篇簡(jiǎn)單記錄自己對(duì)protobuf協(xié)議的學(xué)習(xí)筆記, 上篇簡(jiǎn)單介紹protobuf, 記錄了ubuntu系統(tǒng)中的安裝過(guò)程, 并舉了一個(gè)簡(jiǎn)單示例; 下篇將介紹protobuf的兩個(gè)重要機(jī)制, 動(dòng)態(tài)編譯與類(lèi)型反射.
能力有限, 如有疏漏望請(qǐng)指正.
引子
Google Protocol Buffer協(xié)議簡(jiǎn)稱(chēng)Protobuf, 是谷歌內(nèi)部的混合語(yǔ)言數(shù)據(jù)標(biāo)準(zhǔn)
是一種高效輕便的結(jié)構(gòu)化數(shù)據(jù)存儲(chǔ)格式, 可用于將結(jié)構(gòu)化數(shù)據(jù)串行化(序列化)
適合用于數(shù)據(jù)存儲(chǔ)或RPC數(shù)據(jù)交換格式(不同平臺(tái), 不同機(jī)器之間的信息傳遞), 可用于通訊協(xié)議, 數(shù)據(jù)存儲(chǔ)等領(lǐng)域的語(yǔ)言無(wú)關(guān), 平臺(tái)無(wú)關(guān), 可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)格式
提供多種語(yǔ)言支持, C++, Java, Python, C# ...(protobuf2的話(huà)只支持前三種)
缺陷 可讀性差, 因?yàn)槭且远M(jìn)制形式存放的, 如果不配合對(duì)應(yīng)的proto文件, 那么是無(wú)法看懂的. 這一點(diǎn)與XML截然相反(XML具有自描述性, 就是人能看懂的意思)
- protobuf是一種序列化協(xié)議
- 高效輕便
- 支持多語(yǔ)言
一 簡(jiǎn)介(隨意跳過(guò))
本小節(jié)本著不求甚解的原則簡(jiǎn)單講一下使得protobuf更高效輕便的技術(shù)
其中1,1, 1.2, 1.3解釋了為什么protobuf序列化數(shù)據(jù)非常簡(jiǎn)潔、緊湊,與XML相比,其序列化之后的數(shù)據(jù)量約為1/3到1/10****
其中1.4, 解釋了為什么****解析速度快,比XML快約20-100倍****
1.1 varint編碼方式
用一個(gè)多個(gè)字節(jié)來(lái)表示一個(gè)數(shù)字, 值越小采用的字節(jié)數(shù)越少
每個(gè)字節(jié)最高位用作標(biāo)記位, 如果該bit為1, 表示后續(xù)部分也是該數(shù)字的一部分; 如果該bit為0, 那么表示結(jié)束. (這一點(diǎn)是varint編碼的基本原理)
采用小端字節(jié)序

1.2 Key-Pair存儲(chǔ)方式
采用此種key-value格式無(wú)需使用分隔符來(lái)分隔不同的field
通過(guò)key來(lái)標(biāo)識(shí)field, 通過(guò)對(duì)應(yīng)的key獲取到對(duì)應(yīng)的value

1.3 ZigZag編碼
-
使用無(wú)符號(hào)數(shù)來(lái)表示有符號(hào)數(shù)
- 正負(fù)數(shù)交替出現(xiàn)的方式
優(yōu)點(diǎn) 絕對(duì)值較小的數(shù), 無(wú)論正負(fù), 所需要用的比特位都會(huì)較少, 充分利用了varint編碼的優(yōu)點(diǎn)

1.4 解封包機(jī)制
-
XML的解封包機(jī)制, XML需要從文件中讀取字符串, 然后轉(zhuǎn)換為XML文檔對(duì)象結(jié)構(gòu)模型, 再?gòu)脑撃P椭凶x取制定節(jié)點(diǎn)的字符串, 最后再將該字符串轉(zhuǎn)換為制定類(lèi)型的變量.
- 總之很復(fù)雜啦
-
Protobuf的解封包機(jī)制, 經(jīng)由protobuf序列化得到的結(jié)構(gòu)是以二進(jìn)制的形式保存在文件中, 只需將對(duì)應(yīng)的二進(jìn)制讀取進(jìn)入C++結(jié)構(gòu)類(lèi)型中即可, 就是一個(gè)移位操作
- 顯然更加高效, 當(dāng)然也造成了毫無(wú)自描述性的問(wèn)題...
二 安裝
系統(tǒng) ubuntu-16.04.4
protobuf 此處安裝的protobuf3.6.0, github上隨便一搜就有啦
解壓, 切換至下載目錄
tar -xvf protobuf-all-3.6.0.tar.gz protobuf-3.6.0/根據(jù)不同格式選擇不同的解壓命令啦切換至目標(biāo)目錄下
cd protobuf-3.6.0/觀(guān)察一下有沒(méi)有包含
configure文件, 如果不存在那么先執(zhí)行./autogen.shshell腳本生成該文件./configure此處可以通過(guò)后綴--prefix=$INSTALL_DIR來(lái)指定想要安裝的目標(biāo)目錄(將$INSTALL_DIR替換成自己想要的目錄就好啦), 如果不指定一般是安裝在默認(rèn)的/usr/local/lib中make編譯一下, 等個(gè)十來(lái)分鐘吧make checkmake install安裝protoc --version檢查一下是否安裝成功, 成功會(huì)顯示版本號(hào), 多半會(huì)失敗啦, 那就看下一步-
如果提示
protoc: error while loading shared libraries: libprotoc.so.9: cannot open shared object file: No such file or directory, 那么是因?yàn)閜rotobuf的安裝路徑(默認(rèn)的/usr/local/lib)不在ubuntu體系中默認(rèn)的LD_LIBRARY_PATH中, 所以無(wú)法找到對(duì)應(yīng)的lib. 解決 只需要在/etc/ld.so.conf.d/目錄下創(chuàng)建文件bprotobuf.conf文件, 并寫(xiě)入我們的安裝路徑(/usr/local/lib), 然后執(zhí)行sudo ldconfig即可.- 其中
ldconfig是一個(gè)動(dòng)態(tài)鏈接庫(kù)管理命令,為了讓動(dòng)態(tài)鏈接庫(kù)為系統(tǒng)所共享- 往
/lib和/usr/lib中添加動(dòng)態(tài)庫(kù), 那么只需要執(zhí)行ldconfig即可 - 除了此二目錄以外的位置增加動(dòng)態(tài)庫(kù)時(shí), 需要額外修改
/etc/ld.so.conf或者在/etc/ld.so.conf.d目錄下創(chuàng)建包含對(duì)應(yīng)目錄的.conf文件, 而后執(zhí)行ldconfig
- 往
- 其中
至此protobuf就算是安裝完成啦, 使用
protoc --version再檢查一下試試
如果上述安裝步驟中遇到權(quán)限問(wèn)題, 那么可以使用sudo命令, 或者切成root(慎用root 哈哈哈)
三 簡(jiǎn)單示例
編寫(xiě)簡(jiǎn)單的proto文件
// test.proto
syntax = "proto3";
package lm;
message helloworld
{
int32 id = 1;
string name = 2;
repeated string hero = 3;
}
protoc編譯
protoc -I=./ --cpp_out=./ ./test.proto
其中
-I后接import的根目錄,--cpp_out生成文件的目錄, 最后一個(gè)參數(shù)表示待編譯的文件編譯后將會(huì)生成一個(gè)
.pb.cc文件和一個(gè).pb.h文件
使用示例
#include <iostream>
#include <fstream>
#include "test.pb.h"
int main()
{
lm::helloworld ob1, ob2;
ob1.set_id(1);
ob1.set_name("szw");
ob1.add_hero("loong");
ob1.add_hero("uk");
printf("ob1\n");
ob1.PrintDebugString();
std::ofstream fout("test.txt");
if (!ob1.SerializeToOstream(&fout)){
perror("ob1 SerializeToOstream Wrong!\n");
exit(-1);
}
fout.close();
std::ifstream fin("test.txt");
if(!ob2.ParseFromIstream(&fin)){
perror("ob2 ParseFromIstream Wrong!\n");
exit(-1);
}
fin.close();
printf("ob2\n");
ob2.PrintDebugString();
return 0;
}
makefile
all:
g++ -std=c++11 -c -o test.o test.cpp
g++ -std=c++11 -c -o test.pb.o test.pb.cc
g++ -std=c++11 -o test test.o test.pb.o -lprotobuf -lpthread
clean:
rm -rf *.o
執(zhí)行結(jié)果
ob1
id: 1
name: "szw"
hero: "loong"
hero: "uk"
ob2
id: 1
name: "szw"
hero: "loong"
hero: "uk"