protobuf message定义_ProtoBuf 协议设计与开发
?????周日本來要去爬山的,但是沒去成,突然想寫點東西,但本人文采不好,只能閑扯一點技術方面的文章,整理了下有道筆記,然后最近一直在開發protobuf的協議接口,就寫寫ProtoBuf相關的東西吧。
本文精髓:
? ?protobuf的消息設計
? ?消息分發設計Message Dispatch
? ?針對程序升級的proto設計
文章末尾對著三點做詳細說明。
?????最近一段時間在寫linux服務端接口程序,剛開始如果按照需求的話,大概有十個接口左右,但是后面慢慢分解需求后,其實真正就只有五個接口。
????編碼工作早早完成,進入到測試階段,一般情況可能會等web實現完成后,然后借助web client,在做調試,但是這期間工作效率不高,而且存在很多問題,可能后臺接口沒實現好,也有可能web沒實現好。作為linux后臺服務開發,需要會模擬客戶單發送數據,如果接口很簡單的話,可以直接使用telnet工具。
????現在最為流行的后臺服務端通信的協議有:JSON、XML、ProtoBuf,當協議為這三種的時候,簡單的telnet就不能勝任了,使用JSON和ProtoBuf的話,先借助工具做序列化工作,使用XML的話,也要事先編寫好XML。
????為了更加高效的對后臺服務接口做好單元測試,也為了在web開發調試的時候,提供穩定的后臺服務接口,自己利用MFC寫了一個小的調試工具
??
????參數設置里面輸入的是json串,因為這個有很多工具方便序列化,如:https://www.bejson.com/jsoneditoronline/
????這個工具很簡單,從界面就三個輸入,IP、port、消息類型,主要工作就是模擬客戶端向服務端發送消息:
????????需要借用服務端的proto協議文件
????????序列化protobuf
????????根據消息類型做消息分發
????這里用到protobuf,先大概說一下它的使用與原理
????1,介紹安裝
????????直接去百度,這里就跳過
????2,編寫 .proto文件
????????來個例子:
????????package lm;
????????message helloworld
????????{
????????required int32 ? ? id = 1; ?// ID
????????required string ? ?str = 2; ?// str
????????optional int32 ? ? opt = 3; ?//optional field
????????}
????限定修飾符 + 數據類型 + 字段名稱 = 字段編碼值
????盡量養成良好測編程習慣,對proto文件命名與消息名做規范的命名
????在上例中,package 名字叫做 lm,定義了一個消息 helloworld,該消息有三個成員,類型為 int32 的 id,另一個為類型為 string 的成員 str。opt 是一個可選的成員,即消息中可以不包含該成員。
????3,編譯.proto文件
????寫好 proto 文件之后就可以用 Protobuf 編譯器將該文件編譯成目標語言了。本例中我們將使用 C++。假設您的 proto 文件存放在 $SRC_DIR 下面,您也想把生成的文件放在同一個目錄下,則可以使用如下命令:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
命令將生成兩個文件:
????????lm.helloworld.pb.h , 定義了 C++ 類的頭文件
????????lm.helloworld.pb.cc , C++ 類的實現文件
????在生成的頭文件中,定義了一個 C++ 類 helloworld
????4,序列化
????在第三部編譯生成的cc文件中,有一系列的SerializeToXXX方法,如SerializeToArray,可以根據具體情況用這一系列方法進行序列化。
????5,反序列化
????在第3個步驟編譯生成的cc文件中,有一系列的ParseFromXXX方法,如ParseFromArray,可以根據具體情況用這一系列方法進行反序列化。
????掌握以上幾個步驟基本就能搞定proto的開發了,進階的話可以去掌握proto的數據類型以及requried、optional、repeated限定修飾符,和message的嵌套(message嵌套可以設計出更多復雜的協議,滿足更復雜的需求)。
??? protobuf的優劣自己去百度,JSON,XML我也有用過,但是相對來說,谷歌的protobuf是我用起來最方便高效的。
????這是網上的一個測試結果:http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
????回到文章開頭部分說的精髓,現在逐一到來
????一,proto的設計:
????一般設計規則如下
????message Request
????{
????required fixed64 msgtype = 1;
????required bytes bodys = 2;
????}
????一個消息類型加一個消息體,但這不能滿足復雜的業務需求,所以復雜的系統里面一般拆成這樣:
????message Header
????{
????required fixed64 msgtype = 1;
????}
????message HelloworldRequest
????{
????required int32 ? ? id = 1; ?// ID
????required string ? ?str = 2; ?// str
????optional int32 ? ? opt = 3; ?//optional field
????}
????一個消息類型(單獨定義一個文件)對應一個請求消息,一個請求消息對應一個接口,消息里面的字段對應接口所需要的參數,這是常用的設計方法,能滿足所有的業務需求。
????二,消息分發的設計
這里的message dispatch是指程序根據不同的msgtype,反序列化proto和做不同的業務邏輯處理。
????古老而又傳統的設計是采用switch case來做(我曾經看到有在用if else的),這種方法有很多不足之處,我這里舉幾個很常見的:
????1,代碼臃腫,隨著消息類型的增加,會有n多的case
??? 2,假設case里漏掉了break;那就不妙了。
??? 3,代碼維護差,每次增加msgtype,除了實現對應的業務邏輯處理,還要到消息入口增加對應的case。
????在c語言里面,有一種很實用的辦法,那就是函數指針,很多開源的和上層應用的回調函數或方法的底層都是封裝了c語言的函數指針,這里提到函數指針,我簡單介紹一下(本文沒有用很大篇幅來說明函數指針,掌握其基本定義,慢慢學會衍生到復雜的概念):
????????從概念上說,函數指針是指向函數的指針變量,它本質上是一個指針變量。
????????其廣泛的定義是:int (*f) (int x);
????????復制和調用:int func(int x); ? f = func;
????那么函數指針在Message Dispath 如何設計呢,還是來一個簡單的例子:
????定義一個結構體
????????struct SMsgCmd {
????????int msgtype; ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*msg type*/
????????int (*func)(const char *argv); ? ? ? ? ? /* handler * 函數指針/
????????};
????定義一個結構體數組,并初始化
????????static struct SMsgCmd commands[] = {
????????{ 1, ? Request1},
????????{ 2, ? Request1},
????????};
????在服務程序的消息入口處,遍歷改數組即可
????????for ()
????????{
????????if (msgtype == commands[i].msgtype){
????????(*commands[i].func)(argv);
????????}
????????}
????在c++11里面,可以利用std::function 和std::bind,其原理跟函數指針一個道理。
????三,針對程序升級的proto設計
????程序升級是常有的事,但我們升級的時候需要考慮兼容性,之前有看到過同個版本號如
????if (version == 1){
????}
????else if (version > 2)
????.........
????這樣做的缺陷我就不在過多的說明了。
????對于proto的協議來說,我們只要做到以下幾點,就會完美兼容新舊版本
????????1,不要隨意添加或刪除 required限定詞修飾的字段
????????2,不要隨意改變現有字段編碼值
????????3,若需要新增字段,請用optional限定詞修飾
本文到此就算結束了,若有錯誤之處,請多多指教!
歡迎關注本人微信公眾號:lzyTalk江湖,不只是談江湖,還會分享很多技術干貨哦!
總結
以上是生活随笔為你收集整理的protobuf message定义_ProtoBuf 协议设计与开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++读取.dat文件_MySQL 数据
- 下一篇: 经纬度画轨迹图_HYSPLIT后向轨迹制