Python multiprocessing 模块解析 (4) – managers proxy篇(1)(从 register 开始)

前言:
工作中不少场景是开发后台脚本独, 比如定时job, 扫表, 事件消费等任务.
Python 是首选语言, 其次才是编译型的Golang(有性能要求的任务). 多数
时候单进程脚本足够执行任务, 少数如高频的UGC社区顶踩动作, api 层
将动作写入消息队列异步处理(mq-> job平台 or 自管理的消费脚本进程),
单进程脚本消费能力跟不上, 如果考虑坚持用 Python 情况下同时部署
多个脚本进程消费或者基于 multiprocessing 都是合适的解决方案. 这里
就引出了 multiprocessing 模块, 既然与该模块常打交道, 那么可以带着
好奇心去看看模块的实现 :) 本文以 Python3.5探索其机制

前一篇介绍了 managers 模块的 Server 以及 CS的通信结构.
本篇介绍其通信方式: proxy

再回顾下 multiprocessing/managers.py 源码, 最后模块调用
SyncManger.register, 将常用数据结构注册. 存储到 cls._registry 字典中.
register 是一个 class method:

Manager 类的 _registry 实际是 typeid 的值作为 key, 映射的 value 保存一个元祖:

cls._registry[typeid] = ( callable, exposed, method_to_typeid, proxytype )

  • callable: 即 typeid 对应名字数据结构(通常是标准模块的类):

    eg: typeid = ‘Queue” callable = queue.Queue

  • proxytype: 代理的类型

    未指定是默认是 AutoProxy

  • exposed: tuple, 暴露对外的方法名集合.

    如果 manager client 调用对应类型的方法名不在其中, 抛出 AttributeError

  • method_to_typeid: 模块中只有 BasePoolProxy 定义的映射.

    若 method_to_typeid 非空(即dict),
    exposed = list(exposed) + list(method_to_typeid) list(method_to_typeid)
    即等价于 list(method_to_typeid.keys())
    exposed 最后的结果即 对外暴露(client可使用)的方法的集合.

  • create_method 当 create_method 为 True 时, 创建生成 proxy 实例的方法,

    并以 typeid 的值为函数名, 同时作为 Manager 类的成员方法(见源码的 temp 函数).

这些参数保留后使用有两处:
1. Server.create 使用到了 callable, exposed, method_to_typeid.
server 进程里使用: server accept_connection 时创建 server_client,
server_client 调用 Server.create.
首先在 client 端获取一个 manager 提供的数据结构实例:

eg: task_q = manager.Queue()
此时相当于创建了一个对 server 的连接,Server.create 做的事情就是,
callable 获取真正的实例( obj = queue.Queue() ), 并把实例保存在 server id_to_obj 中维护.
同时维护一个引用计数(防止不期望的垃圾回收). 同时 server 返回给 client 该实例可使用的方法
(即 exposed 的集合, 其实 manager 获取的数据结构实例只是一个 proxy 实例,
但通过 exposed 集合的结果, proxy 行为方式就与就像原数据结构一致, 典型的代理模式).

2. BaseProxy._callmethod 获取 proxytype. client 触发调用.

小结:
1. manager 维护共享数据的方式是通过 Server 实例的
id_to_obj(数据对象映射) 以及 id_to_refcount(数据对象引用计数).
自己维护一套引用计数的目的是当 client 进程不再使用数据时, 可以及时删除对象.
server 本身并不知道何时才是合适的删除时机.
2. proxy 在 client 端作为维护与 server 连接, 以及通信的角色.

  • server 维护的对象 obj 与 proxy 之间使用了比较 trick 的方式, obj 创建时提前 incref 引用计数.

  • client 端 proxy 真正创建后 incref, 实际上 obj 计数为 2,
    因此在 proxy 完全创建完成后, 需要 decref,
    以保证 obj 引用计数是与 proxy 数量相等的(即使用 obj 的 client 数量).

3. BaseManager.register 函数主要是 SyncManager 使用(我们可以继承 BaseManager 实现自己的逻辑).

发表评论

电子邮件地址不会被公开。 必填项已用*标注