接口和协议组成 詹聪聪:一种动态引入 protobuf 对象并复用其实例的方法

一江春水zcc · 2020年11月13日 · 最后由 一江春水zcc 回复于 2020年11月18日 · 3695 次阅读

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()
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 2 条回复 时间 点赞

思路不错,按需加载省空间且提升性能。点赞

陈恒捷 回复

感谢肯定😁

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册