protobuf簡(jiǎn)介及使用流程
1. Protobuf是什么
ProtoBuf(全稱Protocol Buffer)是數(shù)據(jù)結(jié)構(gòu)序列化和反序列化框架,它具有以下特點(diǎn):
- 語(yǔ)??關(guān)、平臺(tái)無(wú)關(guān):即 ProtoBuf ?持 Java、C++、Python 等多種語(yǔ)?,?持多個(gè)平臺(tái)
- ?效:即? XML 更小、更快、更為簡(jiǎn)單
- 擴(kuò)展性、兼容性好:你可以更新數(shù)據(jù)結(jié)構(gòu),而不影響和破壞原有的舊程序
2. Protobuf使?流程介紹
- 編寫(xiě) .proto ?件,?的是為了定義結(jié)構(gòu)對(duì)象(message)及屬性內(nèi)容。
- 使? protoc 編譯器編譯 .proto ?件,?成?系列接?代碼,存放在新?成頭?件和源?件中。
- 依賴?成的接?,將編譯?成的頭?件包含進(jìn)我們的代碼中,實(shí)現(xiàn)對(duì) .proto ?件中定義的字段進(jìn)行設(shè)置和獲取,和對(duì) message 對(duì)象進(jìn)行序列化和反序列化
3. ProtoBuf快速上手
我們以?個(gè)簡(jiǎn)單通訊錄的實(shí)現(xiàn)來(lái)驅(qū)動(dòng)對(duì)Protobuf的學(xué)習(xí)。在通訊錄demo中,我們將實(shí)現(xiàn):
- 對(duì)?個(gè)聯(lián)系?的信息使? Protobuf 進(jìn)?序列化,并將結(jié)果打印出來(lái)。
- 對(duì)序列化后的內(nèi)容使? Protobuf 進(jìn)?反序列,解析出聯(lián)系?信息并打印出來(lái)。
- 聯(lián)系?包含以下信息: 姓名、年齡。
通過(guò)通訊錄demo,我們能快速的了解ProtoBuf的使?流程。
3.1 創(chuàng)建 .proto ?件
- 創(chuàng)建 .proto ?件時(shí),?件命名應(yīng)該使?全?寫(xiě)字?命名,多個(gè)字?之間? _ 連接。 例如:lower_snake_case.proto
- 書(shū)寫(xiě) .proto ?件代碼時(shí),應(yīng)使? 2 個(gè)空格的縮進(jìn)。
我們?yōu)橥ㄓ嶄?demo 新建?件: contacts.proto
3.2 添加注釋
向?件添加注釋,可使? // 或者 /* … */
3.3 具體編寫(xiě)
指定 proto3 語(yǔ)法:
Protocol Buffers 語(yǔ)?版本3,簡(jiǎn)稱 proto3,是 .proto ?件最新的語(yǔ)法版本。proto3 簡(jiǎn)化了 ProtocolBuffers 語(yǔ)?,既易于使?,?可以在更?泛的編程語(yǔ)?中使?。它允許你使? Java,C++,Python等多種語(yǔ)??成 protocol buffer 代碼。在 .proto ?件中,要使? syntax = “proto3”; 來(lái)指定?件語(yǔ)法為 proto3,并且必須寫(xiě)在除去注釋內(nèi)容的第??。 如果沒(méi)有指定,編譯器會(huì)使?proto2語(yǔ)法。
在通訊錄 demo 的 contacts.proto ?件中,可以為?件指定 proto3 語(yǔ)法,內(nèi)容如下:
syntax = "proto3";
package 聲明符:
package 是?個(gè)可選的聲明符,能表? .proto ?件的命名空間,在項(xiàng)?中要有唯?性。它的作?是為了避免我們定義的消息出現(xiàn)沖突。
在通訊錄 demo 的 contacts.proto ?件中,可以聲明其命名空間,內(nèi)容如下:
package contacts;
定義消息(message):
消息(message): 要定義的結(jié)構(gòu)化對(duì)象,我們可以給這個(gè)結(jié)構(gòu)化對(duì)象中定義其對(duì)應(yīng)的屬性內(nèi)容。在網(wǎng)絡(luò)傳輸中,我們需要為傳輸雙?定制協(xié)議。定制協(xié)議說(shuō)?了就是定義結(jié)構(gòu)體或者結(jié)構(gòu)化數(shù)據(jù),?如,tcp,udp 報(bào)?就是結(jié)構(gòu)化的。再?如將數(shù)據(jù)持久化存儲(chǔ)到數(shù)據(jù)庫(kù)時(shí),會(huì)將?系列元數(shù)據(jù)統(tǒng)??對(duì)象組織起來(lái),再進(jìn)?存儲(chǔ)。ProtoBuf 就是以 message 的方式來(lái)?持我們定制協(xié)議字段,后期幫助我們形成類和?法來(lái)使用。
在通訊錄 demo 中我們就需要為聯(lián)系人定義?個(gè) message:
.proto ?件中定義?個(gè)消息類型的格式為:
message 消息類型名{ } 消息類型命名規(guī)范:使?駝峰命名法,?字??寫(xiě)。
為 contacts.proto(通訊錄 demo)新增聯(lián)系?message
syntax = "proto3"; package contacts; // 定義聯(lián)系?消息 message PeopleInfo { }
定義消息字段:
在 message 中我們可以定義其屬性字段,字段定義格式為:字段類型 字段名 = 字段唯?編號(hào);
- 字段名稱命名規(guī)范:全?寫(xiě)字?,多個(gè)字?之間? _ 連接。
- 字段類型分為:標(biāo)量數(shù)據(jù)類型 和 特殊類型(包括枚舉、其他消息類型等)。
- 字段唯?編號(hào):?來(lái)標(biāo)識(shí)字段,?旦開(kāi)始使?就不能夠再改變。
樣例:
// 聲明語(yǔ)法版本 syntax = "proto3"; // 聲明代碼的命名空間 package contacts; //結(jié)構(gòu)化對(duì)象的描述 message PeopleInfo{ // 各個(gè)字段描述: 字段類型 字段名 = 字段唯一編號(hào) string name = 1; int32 age = 2; }
注:這?還要特別講解?下字段唯?編號(hào)的范圍:1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可?。
19000 ~ 19999 不可?是因?yàn)椋涸?Protobuf 協(xié)議的實(shí)現(xiàn)中,對(duì)這些數(shù)進(jìn)?了預(yù)留。如果?要在.proto?件中使?這些預(yù)留標(biāo)識(shí)號(hào),例如將 name 字段的編號(hào)設(shè)置為19000,編譯時(shí)就會(huì)報(bào)警。
值得?提的是,范圍為 1 ~ 15 的字段編號(hào)需要?個(gè)字節(jié)進(jìn)?編碼, 16 ~ 2047 內(nèi)的數(shù)字需要兩個(gè)字節(jié)進(jìn)?編碼。編碼后的字節(jié)不僅只包含了編號(hào),還包含了字段類型。所以 1 ~ 15 要?來(lái)標(biāo)記出現(xiàn)?常頻繁的字段,要為將來(lái)有可能添加的、頻繁出現(xiàn)的字段預(yù)留?些出來(lái)。
3.4 編譯 contacts.proto 文件
編譯命令?格式為:
protoc [--proto_path=IMPORT_PATH] --cpp_out=DST_DIR path/to/file.proto protoc 是 Protocol Buffer 提供的命令?編譯?具。 --proto_path 指定被編譯的.proto?件所在?錄,可多次指定??珊?jiǎn)寫(xiě)成 -I IMPORT_PATH 。如不指 定該參數(shù),則在當(dāng)前?錄進(jìn)?搜索。當(dāng)某個(gè).proto ?件 import 其他.proto ?件時(shí), 或需要編譯的 .proto ?件不在當(dāng)前?錄下,這時(shí)就要?-I來(lái)指定搜索?錄。 --cpp_out= 指編譯后的?件為 C++ ?件。 OUT_DIR 編譯后?成?件的?標(biāo)路徑。 path/to/file.proto 要編譯的.proto?件
編譯 contacts.proto ?件命令如下:
protoc --cpp_out=. contacts.proto
編譯 contacts.proto ?件后,會(huì)?成所選擇語(yǔ)?的代碼,我們選擇的是C++,所以編譯后?成了兩個(gè)文件: contacts.pb.h contacts.pb.cc 。
對(duì)于編譯?成的 C++ 代碼,包含了以下內(nèi)容 :
- 對(duì)于每個(gè) message ,都會(huì)?成?個(gè)對(duì)應(yīng)的消息類
- 在消息類中,編譯器為每個(gè)字段提供了獲取和設(shè)置?法,以及?下其他能夠操作字段的方法
- 編輯器會(huì)針對(duì)于每個(gè) .proto ?件?成 .h 和 .cc ?件,分別?來(lái)存放類的聲明與類的實(shí)現(xiàn)
contacts.pb.h 部分代碼展示:
class PeopleInfo final : public ::PROTOBUF_NAMESPACE_ID::Message { public: using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; void CopyFrom(const PeopleInfo& from); using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; void MergeFrom( const PeopleInfo& from) { PeopleInfo::MergeImpl(*this, from); } static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { return "PeopleInfo"; } // string name = 1; void clear_name(); const std::string& name() const; template <typename ArgT0 = const std::string&, typename... ArgT> void set_name(ArgT0&& arg0, ArgT... args); std::string* mutable_name(); PROTOBUF_NODISCARD std::string* release_name(); void set_allocated_name(std::string* name); // int32 age = 2; void clear_age(); int32_t age() const; void set_age(int32_t value); };
上述的例?中:
- 每個(gè)字段都有設(shè)置和獲取的方法, getter 的名稱與?寫(xiě)字段完全相同,setter ?法以 set_ 開(kāi)頭。
- 每個(gè)字段都有?個(gè) clear_ 方法,可以將字段重新設(shè)置回 empty 狀態(tài)。
contacts.pb.cc 中的代碼就是對(duì)類聲明?法的?些實(shí)現(xiàn),在這?就不展開(kāi)了。
到這?有人可能就有疑惑了,那之前提到的序列化和反序列化?法在哪?呢?在消息類的?類MessageLite 中,提供了讀寫(xiě)消息實(shí)例的方法,包括序列化?法和反序列化?法。
class MessageLite { public: //序列化: bool SerializeToOstream(ostream* output) const; // 將序列化后數(shù)據(jù)寫(xiě)??件 流 bool SerializeToArray(void *data, int size) const; bool SerializeToString(string* output) const; //反序列化: bool ParseFromIstream(istream* input); // 從流中讀取數(shù)據(jù),再進(jìn)?反序列化 動(dòng)作 bool ParseFromArray(const void* data, int size); bool ParseFromString(const string& data); };
注意:
- 序列化的結(jié)果為?進(jìn)制字節(jié)序列,???本格式。
- 以上三種序列化的?法沒(méi)有本質(zhì)上的區(qū)別,只是序列化后輸出的格式不同,可以供不同的應(yīng)?場(chǎng)景使?。
- 序列化的 API 函數(shù)均為const成員函數(shù),因?yàn)樾蛄谢粫?huì)改變類對(duì)象的內(nèi)容, ?是將序列化的結(jié)果保存到函數(shù)?參指定的地址中
序列化與反序列化的使用:
創(chuàng)建?個(gè)測(cè)試?件 test.cc,?法中我們實(shí)現(xiàn):
- 對(duì)?個(gè)聯(lián)系?的信息使? PB 進(jìn)?序列化,并將序列化結(jié)果打印出來(lái)。
- 對(duì)序列化后的內(nèi)容使? PB 進(jìn)?反序列,解析出聯(lián)系?信息并打印出來(lái)。
#include <iostream> #include "contacts.pb.h" using namespace std; int main() { string people_str; { contacts::PeopleInfo people; people.set_age(20); people.set_name("忘憂"); if(!people.SerializeToString(&people_str)) { cout << "序列化聯(lián)系人失敗" <<endl; } cout << "序列化之后的 people_str: " << people_str << endl; // 反序列化 { contacts::PeopleInfo people; if(!people.ParseFromString(people_str)) { cout << "反序列化聯(lián)系人失敗" <<endl; } cout << "Parse age: " << people.age() << endl; cout << "Parse name: " << people.name() << endl; } } return 0; }
代碼書(shū)寫(xiě)完成后,編譯 test.cc,生成可執(zhí)行程序:
g++ test.cc contacts.pb.cc -o test -std=c++11 -lprotobuf
執(zhí)?可執(zhí)?程序,可以看? people 經(jīng)過(guò)序列化和反序列化后的結(jié)果:
由于 ProtoBuf 是把聯(lián)系?對(duì)象序列化成了?進(jìn)制序列,這?? string 來(lái)作為接收?進(jìn)制序列的容器。所以在終端打印的時(shí)候會(huì)有換?等?些亂碼顯?。另外相對(duì)于 xml 和 JSON 來(lái)說(shuō),因?yàn)镻B被編碼成?進(jìn)制,破解成本增?,ProtoBuf 編碼是相對(duì)安全的。
到此這篇關(guān)于protobuf簡(jiǎn)介及使用流程的文章就介紹到這了,更多相關(guān)protobuf使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot整合ELK實(shí)現(xiàn)日志監(jiān)控
這篇文章主要為大家詳細(xì)介紹了SpringBoot整合ELK實(shí)現(xiàn)日志監(jiān)控的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11詳解mybatis中association和collection的column傳入多個(gè)參數(shù)問(wèn)題
這篇文章主要介紹了詳解mybatis中association和collection的column傳入多個(gè)參數(shù)問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10SpringBoot中的異常處理與參數(shù)校驗(yàn)的方法實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot中的異常處理與參數(shù)校驗(yàn)的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04Spring boot按日切分spring boot的nohup.out日志文件的方法
過(guò)大的日志文件維護(hù)起來(lái)存在諸多問(wèn)題,所以最好是能夠按日或按大小切分日志文件,下面小編給大家?guī)?lái)了Spring boot按日切分spring boot的nohup.out日志文件的方法,一起看看吧2018-08-08Java的字符串中對(duì)子字符串的查找方法總結(jié)
這篇文章主要介紹了Java的字符串中對(duì)子字符串的查找方法總結(jié),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11XFire構(gòu)建web service客戶端的五種方式
本篇文章主要介紹了XFire構(gòu)建web service客戶端的五種方式。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01Java中json與javaBean幾種互轉(zhuǎn)的講解
今天小編就為大家分享一篇關(guān)于Java中json與javaBean幾種互轉(zhuǎn)的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03SpringBoot整合easy-es的詳細(xì)過(guò)程
本文介紹了EasyES,一個(gè)基于Elasticsearch的ORM框架,旨在簡(jiǎn)化開(kāi)發(fā)流程并提高效率,EasyES支持SpringBoot框架,并提供了CRUD操作、批量操作和查詢操作等方法,文章還列舉了使用EasyES時(shí)可能遇到的技術(shù)難題及解決方法,感興趣的朋友一起看看吧2025-02-02Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析
這篇文章主要介紹了Java8新特性Stream流中anyMatch和allMatch和noneMatch的區(qū)別解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01