Protobuf動態解析

2021-08-22 19:08:45 字數 3673 閱讀 2520

閱讀目錄

回到頂部

在接收到 protobuf 資料之後,如何自動建立具體的 protobuf message 物件,再做反序列化。「自動」的意思主要有兩個方面:(1)當程式中新增乙個 protobuf message 型別時,這部分**不需要修改,不需要自己去註冊訊息型別,不需要重啟程序,只需要提供protobuf檔案;(2)當protobuf message修改後,這部分**不需要修改,不需要自己去註冊訊息型別,不需要重啟程序只需要提供修改後protobuf檔案。

回到頂部

protobuf的動態解析在google protobuf buffer官網並沒有什麼介紹。通過google出的一些參考文件可以知道,其實,google protobuf 本身具有很強的反射(reflection)功能,可以根據 type name 建立具體型別的 message 物件,我們直接利用即可,應該就可以滿足上面的需求。

實現可以參考**的文章《玩轉protocol buffers 》,裡面對protobuf的動態解析的原理做了詳細的介紹,在此我介紹一下protobuf  class diagram。

大家通常關心和使用的是圖的左半部分:messagelite、message、generated message types (person, addressbook) 等,而較少注意到圖的右半部分:descriptor, descriptorpool, messagefactory。

上圖中,其關鍵作用的是 descriptor class,每個具體 message type 對應乙個 descriptor 物件。儘管我們沒有直接呼叫它的函式,但是descriptor在「根據 type name 建立具體型別的 message 物件」中扮演了重要的角色,起了橋梁作用。上圖的紅色箭頭描述了根據 type name 建立具體 message 物件的過程。

回到頂部

先直接上**,這個**來自於《玩轉protocol buffers 》:

#include 

#include 

#include 

#include 

#include 

using namespace std;

using namespace google::protobuf;

using namespace google::protobuf::compiler;

int main(int argc,const char *argv)

disksourcetree sourcetree;

//look up .proto file in current directory

importer importer(&sourcetree, null);

//runtime compile foo.proto

importer.import("foo.proto");

const descriptor *descriptor =    importer.pool()->

findmessagetypebyname("pair");

cout 

// build a dynamic message by "pair" proto

dynamicmessagefactory factory;

const message *message = factory.getprototype(descriptor);

// create a real instance of "pair"

message *pair = message->new();

// write the "pair" instance by reflection

const reflection *reflection = pair->getreflection();

const fielddescriptor *field = null;

field = descriptor->findfieldbyname("key");

reflection->setstring(pair, field,"my key");

field = descriptor->findfieldbyname("value");

reflection->setuint32(pair, field, 1111);

cout 

delete pair;

return0;

那我們就來看看上面的**

disksourcetree sourcetree;

//look up .proto file in current directory

importer importer(&sourcetree, null);

//runtime compile foo.proto

importer.import("foo.proto");

const descriptor *descriptor = importer.pool()->findmessagetypebyname("pair");

const message *message = factory.getprototype(descriptor);

message *pair = message->new();

const reflection *reflection = pair->getreflection(); 

const fielddescriptor *field = null;

field = descriptor->findfieldbyname("key");

reflection->setstring(pair, field,"my key");

field = descriptor->findfieldbyname("value");

reflection->setuint32(pair, field, 1111);

直接copy上面**看起來我們上面的需求就滿足了,只是唯一的缺點就是每次來個包載入一次配置檔案,當時覺得效能應該和讀取磁碟的效能差不多,但是經過測試效能極差,乙個程序每秒盡可以處理1000多個包,經過分析效能瓶頸不在磁碟,而在頻繁呼叫malloc和free上。

動態的message是我們用dynamicmessagefactory構造出來的,因此銷毀message必須用同乙個dynamicmessagefactory。 動態更新.proto檔案時,我們銷毀老的並使用新的dynamicmessagefactory,在銷毀dynamicmessagefactory之前,必須先刪除所有經過它構造的message。

原理:dynamicmessagefactory裡面包含dynamicmessage的共享資訊,析構dynamicmessage時需要用到。生存期必須保持descriptor>dynamicmessagefactory>dynamicmessage。 

釋放順序必須是:釋放所有dynamicmessage,釋放dynamicmessagefactory,釋放importer。

回到頂部

資源釋放前,必須要了解資源的構造原理,通過構造原理反推釋放順序,這樣就少走彎路、甚至不走。

回到頂部

一種自動反射訊息型別的 google protobuf 網路傳輸方案

《玩轉protocol buffers 》

《google protocol buffer 的使用和原理》

如何解析超長的protobuf

在呼叫protobuf的parsefromstring str 方法時,預設情況下,如果str的長度 64mb,會返回失敗。這裡給出了解釋,主要是出於安全因素的考慮。可以通過settotalbyteslimit方法去除這個限制 google protobuf message req google p...

Groovy動態解析

a 前面需要說些什麼嗎?b 不需要嗎?a 需要嗎?解析方式一 通過指定的paths來初始化groovyscriptengine 通過指定的paths來初始化groovyscriptengine string paths groovyscriptengine gse new groovyscripte...

protobuf反射機制

參考 google protocol buffers protobuf 是一款非常優秀的庫,它定義了一種緊湊的可擴充套件二進位制訊息格式,特別適合網路資料傳輸。它為多種語言提供 binding,大大方便了分布式程式的開發,讓系統不再侷限於用某一種語言來編寫。在網路程式設計中使用 protobuf 需...