Python multiprocessing 模块解析 (3) – managers server(CS结构)

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

再回顾 [17.2.2.7. Managers – Python doc]关于 Manager 的定义:

Managers provide a way to create data which can be shared between different processes, including sharing over a network between processes running on different machines. A manager object controls a server process which manages shared objects. Other processes can access the shared objects by using proxies.
Manager 提供了多进程环境下可以通过网络跨进程或者跨机器共享数据.
一个 manager object 控制一个 server 进程用于管理共享数据.
其他进程可以通过代理访问.

此篇探索 manager 模块 BaseManager 的 _Server类以及实例应用场景

1. Manager start 运行 server

回顾 multiprocess.context::BaseContext::Manager 方法的实现:

BaseManger 的 _Server 即 同模块下的 Server 类(不理解为何要设置个别名, 而不是
直接调用 Server). Manager::start 方法做的事情就是 self._process 赋值相应 context
的 Process 实例(eg: 在 Linux 环境的为 ForkContext, 对应 ForkProcess 类).

实际上有两种方式触发 Manager::start 方法:

2. 通过 BaseManager 实例 get_server 方法直接运行 server

阅读文档 Managers get_server :
其中提供代码案例:

样例中首先是获得 server 实例, 再调用 server.serve_forever() 在当前进程中运行.
实际上 Manager::start 最终也是触发 server.serve_forever() 运行, 区别在于
Manager::start 方法实现是把 server 交给 Manager 实例的 _process 成员,
单独使用一个daemon子进程跑 server.

从本案例里可以看到我们可以自定义 manager server 绑定的 address 地址.
官方文档里描述 Manager 的功能本身时, 即表明了服务多进程, 甚至跨机器共享数据.
本质上的是网络通信实现交互, Manager server 管理共享的数据, 其他业务进程与之
通信, 交互数据.
Manager::start 使用的默认配置的地址, 通过 UNIX socket 服务于同个程序里, 父进程
和其 fork 的子进程.
既然要支持跨机器通信, 那么我们模仿案例2的代码, 将绑定地址设置为 ip:port,
即可实现跨机器数据共享(因为通信本身使用 pickle 序列化数据, 因此要求程序的依赖
环境是一致的).

3. 通信的另一边 client

同样官方文档提供的代码样例 Managers connect :

通信结构是典型的CS, manager 实例此时只提供了客户端功能.
完整的 CS 代码样例, 可以参考官方例子: 17.2.2.7.2. Using a remote manager .
Issue7503 提到了在 tcp 访问的方式下并不是很适合, 另外我尝试通过 remote 方式
使用 SyncManager 以及注册的数据结构, 并没能正常访问数据, 因此我们在使用
multiprocessing Manager 管理共享数据的场景里, 跨机器方面通信并不适用,
但在同一个父进程下的多进程服务中是一个不错的选择.

总结:

1. Manager 实际上是提供了一个 server 负责管理多进程数据共享.
2. 使用tcp的方式跨机器访问 Manager 数据并不容易实现.
3. 跨机器访问的前提是代码依赖一致(因为通信交互的数据协议是 pickle).
可以扩展下思路: 相似的应用场景里, flask技术栈中工程师会使用到 flask_cache 这个
插件. 其提供的缓存功能, cache 装饰器, 当我们指定 cache 保存于 mc 或者 redis,
存储的数据为 pickle 序列化后的数据. flask web server 读取缓存时, 要求代码依赖的
环境是一致的(否则 pickle load error).
作为 cache 服务的 mc,redis 相当于 managers::Server 这个类提供的服务.
当然, 在跨机器环境里实现数据共享, 建议是使用 mc,redis 这类服务,
而不是语言级别的 Manager.
理由当然是 mc redis 性能和稳定上秒杀 Python~(尤其是 Py2, 坑多…).
Note: 依赖第三方服务时, 如果要求数据访问一致性, 需要在客户端自己实现锁逻辑.

原创文章,转载请注明: 转载自kaka_ace's blog

本文链接地址: Python multiprocessing 模块解析 (3) – managers server(CS结构)

发表评论

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