筒子們,使用Protobuf優化你的協議


Protocol buffers是google提供的一種將結構化數據進行序列化和反序列化的方法,其優點是語言中立,平台中立,可擴展性好,目前在google內部大量用於數據存儲,通訊協議等方面。PB在功能上類似XML,但是序列化后的數據更小,解析更快,使用上更簡單。用戶只要按照proto語法在.proto文件中定義好數據的結構,就可以使用PB提供的工具(protoc)自動生成處理數據的代碼,使用這些代碼就能在程序中方便的通過各種數據流讀寫數據。PB目前支持Java, C++和Python3種語言。另外,PB還提供了很好的向后兼容,即舊版本的程序可以正常處理新版本的數據,新版本的程序也能正常處理舊版本的數據。我們主要研究PB在C++語言的使用,PB的編譯安裝比較簡單,C++的庫使用make完成,java使用maven完成,python直接使用setup命令完成。

Protocol Buffers要如何用在socket通信的通信協議中?可以大概地說一下:你需要根據你的協議編寫一個.proto文件,此文件的格式是按Protocol Buffers的要求書寫的。然后用Protocol Buffers編譯器生成這個文件對應的類文件(包括一個.h文件和一個.cc文件),然后在你的程序中include生成的頭文件,當需要發送socket消息的時候,先用這個類的對象的SerializeToString()方法,生成一個字符串,這個字符串也就相當於我們傳統意義上的編碼過的消息,然后在socket消息的接收方,使用ParseFromString()方法,就可以將消息中包含的數據解析到生成的類的成員變量中,就可以直接取出來用了。整個過程不需要你去考慮編碼、解碼,就算更改了協議,修改工作也非常方便。

零、為什么要研究ProtoBuff

需求是最大的驅動,加入百度Hi團隊一個多月了,這一個多月的時間里做了許多以前沒有做過的事。針對Hi用戶的反饋,移動端Hi對流量的消耗太大。Hi消息傳輸的格式目前主要有2種,一種是公司定義的一種nshead+mcbody的形式,這種方式類似於我們今天所要講解的ProtoBuff,統一使用這種方式也能夠減小傳輸流量,而且數據安全性也更高;另一中方式就是比較古老的文本協議格式了。公司決定對現有的文本協議進行優化。怎么優化呢?Hi目前使用文本協議在客戶端和服務端進行信息等傳輸,這里所說的消息包括CS之間Request包、ACK包,或者服務端的Notify包等等,這些包里面的包括的內容不僅僅是聊天信息,也包括聯系人狀態變更等各種通知信息。

總得來說,Hi消息傳輸的報文格式可以概括為:header + body,header主要由一些鍵值對構成,而body則更多的是使用xml的格式。最終決定使用高大上的ProtoBuff對當前的文本協議進行改進,在這里先列出具體的實現思路:

  1. 現有的消息包括header和body兩個部分,我們需要一個轉換工具,分析現有報文的定義要求,生成一個消息格式xml文件:即包括了header和body的一個xml文件;
  2. 根據該xml格式生成proto的描述文件,然后通過Proto編譯生成對應的.h和.cc文件;
  3. 最后就是要考慮和現有協議的轉換了,龐大的系統兼容性是不可忽略的,即服務端添加一個專門協議轉換的模塊,分別針對上行和下行進行原有報文到pb報文的相互轉換。

一、ProtoBuff開發三部曲

所謂開發三部曲,即:

  1. 定義描述文件,即.proto文件;
  2. 使用proto編譯器生成該描述文件對應的定義文件,C++包括.h和.cc;
  3. 使用定義文件中提供的ProtoBuff的API讀寫消息。

舉個簡單的實例,百度Hi在拉取群列表時的協議定義為group:get_list,我們在描述文件中定義如下:

image

我們如何去按照這個定義要求去封包呢?直接看代碼:

image

上圖中88行中的日志就是將我們的PB報文以字符串形式打印出來,便於我們調試,即DebugString方法。在日志中我們可到PB結構如下:

image

二、序列化與反序列化

繼續看代碼,我們已經准備好了PB的封包,序列化之后就可以通過連接發出去了

image

百度Hi-Server在接收到PB包之后通過一系列的處理,會返回一個group:get_list的ACK包回來,我們怎么把回包拿出來並轉化成我們現有的文本協議形式呢?繼續看代碼:

image

紅色框圖中的部分就是反序列化的過程,通過反序列化之后,我們就可以調用ProtoBuff定義的API去解析該PB報文了。

三、完成協議的測試

我們的測試目的就是保證PB與原協議的兼容性,換句話說就是轉換后的數據一致性,我們在ImpPacket中重載了==運算符,完成比較的過程,即分別對ImpPacket中的header和body做比較,header是由一些map構成的,body則是xml構成的。我們的比較就是分別對header和body中的map和xml進行比較。

image

xml的比較代碼如下(部分):

image

 

這就是該博文的所有內容,6個不同的協議,耗時2天時間完成,期間還踩到老代碼中的一個坑,gdb調試才定位到,累覺不愛。感謝閱讀,Published by Windows LiveWriter.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM