Protocol Buffers在資料採集與傳輸系統建設方式論文
Protocol Buffers在資料採集與傳輸系統建設方式論文
隨著通訊技術和感測器技術的不斷髮展,資料採集與傳輸系統得到了越來越廣泛的應用。而Google Protocol Buffers是Google公司開發是一款非常優秀的庫,其定義了緊湊的、可擴充套件的二進位制訊息格式,特別適合用於資料傳輸。本文著重介紹了使用Protocol Buffers的對資料的封裝和其反射機制來實現資料採集與傳輸系統的快速擴展采集資料型別。
1 Protocol Buffers概述
1.1 簡介
Protocol Buffers(以下簡稱ProtoBuf)是由Google開發的一種資料描述語言。ProtoBuf定義了一種緊湊的可擴充套件二進位制訊息格式,能對結構化的資料進行靈活的、高效的、自動的機制來進行序列化。ProtoBuf可擴充套件方式的序列化結構資料被廣泛應用在通訊協議、資料儲存等領域。
1.2 ProtoBuf的效能
一條訊息資料,用ProtoBuf序列化後的大小是JSON的十分之一,是XML格式的二十分之一,是二進位制序列化的十分之一。總體看來ProtoBuf的優勢還是非常明顯的。
2 應用在資料採集與傳輸系統中
這裡所設計的資料採集與傳輸系統採用Slave-Master結構。其中Slave負責採集資料並將資料傳送給Master;Master接收所採集的資料並做進一步處理。Slave可以支援多種資料型別(如GPS、影象等)的採集。
2.1 根據不同的採集資料型別,編寫proto檔案
在ProtoBuf中,所有的物件都被視為訊息。訊息的每個屬性描述都可以使用required、optional、repeated來進行描述。ProtoBuf資料描述語言中也支援一些基本的資料型別如string、int32、double等等。
設Slave的採集資料型別有Type1、Type2。這兩種型別的Proto描述命名為MsgType1和MsgType2(圖1所示)。
經proto編譯後,生成的訊息類為MsgType1和MsgType2,它們均繼承自google::protobuf::Message類。
2.2 設計支援不同採集資料型別的資料傳輸格式
在資料傳輸中使用ProtoBuf需要解決兩個問題,一是資料的長度:ProtoBuf打包的資料沒有自帶長度資訊或終結符,這就需要由應用程式自己在發生和接收的時候做正確的分割;二是訊息型別:ProtoBuf打包的資料沒有自帶的型別資訊,在訊息傳輸過程中,傳送方需要將訊息型別告訴接收方,接收方根據訊息型別再做反序列化。對於長度問題,可以將長度資訊作為訊息的一個段來解決。而對於訊息型別問題,可以使用ProtoBuf根據訊息的型別名反射自動建立對應的訊息物件的機制來解決。因此,可以設計基本傳輸格式的格式如圖2所示:
ProtoBuf Message的序列化資料封裝在message_data中,且稱這種資料格式為Message Package(訊息包)。
2.3 訊息打包器的設計
訊息包格式設計完後,首先要對不同的採集資料型別編寫封裝函式,以便將相應型別的資料封裝到對應的ProtoBuf Message中。然後使用訊息打包器將Slave所採集的某種型別的資料資訊打包成上圖的訊息包。訊息打包器先透過ProtoBuf將特定型別的採集資料進行序列化,並生填充Message Data。最後再填充Message Package中的Length 、Message Name等欄位,完成訊息的打包操作。訊息打包器程式碼如下:
std::string CreateMsgPackage( const google::protobuf::Message& msg )
{
std::string msg_pack;
msg_pack.resize( sizeof( int32_t ) );
string& msg_name = msg.GetTypeName();
int32_t name_len = msg_name.size()+1;
msg_pack.append((char*)&name_len,sizeof(name_len));
msg_pack.append(msg_name.c_str(),name_len);
Msg.AppendToString(&msg_pack);
char* begin = msg_pack.c_str()+sizeof( int32_t );
int32_t length = msg_pack.size()-sizeof(int32_t);
std::copy( (char*)( &length ), (char*)( Length ) +
sizeof( Length ), msg_pack.begin );
return msg_pack;
}
2.4 訊息解包器的設計
接收到訊息包之後要進行解封裝,分解出訊息包中的各個欄位,這裡不再詳述。ProtoBuf本身具有很強的反射機制,ProtoBuf可以能根據Message Name建立一個該型別的訊息,然後使用Message Data來反序列化該訊息,從而在Message Package中恢復出相應型別的Message,由此完成對訊息的`識別。由訊息包來還原相應的訊息的程式碼如下:
Message* CreateMsg( std::string& msg_pack )
{
// 從msg_pack中分離msg_name、msg_data等的程式碼從略
Message* msg = NULL;
Descriptor* desc = DescriptorPool::generated_pool()->FindMessageTypeByName(msg_name);
Message* prototype = MessageFactory::generated_factory()->GetPrototype(desc);
msg = prototype->New();
msg->ParseFromArray(msg_data, msg_data_len);
return msg;
}
2.5 訊息分發器的設計
Master在得到相應型別的採集資料訊息後,需要傳遞給相應的訊息處理方法,這就涉及到訊息的分發。訊息分發器可以使用map來實現,由於每個具體訊息型別都有一個全域性的Descriptor物件,其地址是唯一的,可作為key;value為針對特定採集資料型別訊息的處理函式,即std::map,其中MessageCallBack為 boost::function。由於訊息分發器傳給處理函式的引數是Message*型別,處理函式需要對其進行向下轉型後才能使用。訊息分發器在接收到某一訊息後,在map中查詢對應的處理函式,並執行該函式。
2.6 整體結構
在Slave端,使用者需要使用proto資料描述語言描述該型別的資料,併產生相應的Message型別,此外使用者還要編寫相應資料型別訊息封裝方法。在Master端,由於與Slave使用相同的proto檔案,訊息解包器可以分辨出相應型別的Message。使用者在Master端需要編寫針對某具體型別採集資料的處理方法,並向訊息分發器註冊。訊息分發器將訊息解包器解出的訊息作為引數呼叫對應的處理方法。
3 結束語
在Slave-Master結構的系統中透過編寫proto檔案來描述各種型別的採集資料;在Slav e端進行採集資料的序列化和封裝;在Master端編寫對應的採集資料處理方法,並將該方法註冊到Master的訊息分發器中,完成對採集資料型別的快速擴充套件。
Protocol Buffers .https://developers.google.com/protocol-buffers/docs/overview.
陳碩.Linux多執行緒服務端程式設計——使用muduo C++網路庫.北京:電子工業出版社.2013:220-236.
李紀欣,王康,周立法,章軍.Google Protobuf在Linux Socket通訊中的應用.電腦開發與應用.2013,26(4).
田源,潘晨光,丁傑.Protocol Buffers在即時通訊系統中的應用研究 .現代電子技術.2013,37(5)