詹聰聰:一種動態引入 protobuf 物件並複用其例項的方法

一江春水zcc發表於2020-11-13

1.引言

基於protobuf的訊息傳輸,一般都有專門的資料結構去儲存protobuf物件。類似於下圖這種,在程式執行中往往需要一次性將所有的protobuf物件載入到記憶體中。再去初始化物件例項來使用,並且物件例項也沒有複用,效率低下。

protocol_dict

但是我們測試人員來講,根本不需要用到所有的協議。尤其是在對協議進行壓力測試的時候,寶貴的CPU資源和記憶體資源浪費在一些可能永遠也不會用到的協議上,是非常可惜的。但是,不引入協議是沒有辦法解析protobuf的二進位制流的。為了解決這個矛盾,我利用python的動態載入的語言特性,設計了一種動態引入protobuf物件並複用其例項的方法。

2.設計思路

構造一個物件池,按需引入protobuf物件。物件的引入時機是在protobuf物件首次被使用的時候,從對應的protobuf的py檔案中引入。

2.1預處理

事先遍歷所有protobuf自動生成的py檔案,蒐集所有物件,以json字典的形式寫入到protocol_dict.py中備用。本操作屬於預處理,且只有一次。如下圖所示,json字典存的只是物件的名稱,以字串的形式存放,程式執行時載入很快。

protocol_dict

2.2 協議物件池的資料結構定義

協議物件池的資料結構如下:

protocol_pool = {
str(protocol_num): {
"file_name": "prototocol_xxx_pb2",
"protocol_name": "xxx"
"instance": xxx_instance
},
}

2.3 按需載入

引入模組的所需的python庫為importlib

import importlib
protocol_module = importlib.import_module(file_name)

找到模組中的類,需要類的名稱,還需要藉助模組的__dict__屬性

protocol = protocol_module.__dict__.get(protocol_name)

最後返回物件的例項。

3. 結語

這裡只提供了一種複用的思路,歡迎各種建設性意見,以及其他程式語言的實現。

4.參考程式碼

file_name: protcol_manager.py

import importlib


class ProtocolManager(object):
protocol_instance_pool = dict()

def get_protocol_instance(self, protocol_num):
"""獲取協議物件例項
:param protocol_num: 協議號
:return: 協議物件
"""

instance_dict = self.protocol_instance_pool.get(str(protocol_num))
if instance_dict is None:
from app.utils import protocol_dict
instance_dict = protocol_dict.protocol_dict.get(str(protocol_num))
if instance_dict is None:
return None
else:
self.__add_instance(instance_dict)
self.protocol_instance_pool.update({
str(protocol_num): instance_dict
})
if instance_dict.get("instance") is None:
self.__add_instance(instance_dict)
return instance_dict.get("instance")

@staticmethod
def __add_instance(instance_dict):
"""新增物件例項
:param instance_dict: 物件池的一個元素
"""

file_name = instance_dict.get("file_name")
protocol_name = instance_dict.get("protocol_name")
protocol_module = importlib.import_module(file_name)
protocol = protocol_module.__dict__.get(protocol_name)
instance_dict.update({"instance": protocol()})


protocol_manager = ProtocolManager()

相關文章