在队列中进行并行处理(使用Celery中的Pool)
我在用Celery来处理我做的CGI应用中的任务队列。我设置的方式是,Celery每次只运行一个或两个任务,通过设置CELERYD_CONCURRENCY = 1
或= 2
(这样可以避免处理器过载或内存消耗过大)。这个队列运行得很好,多亏了我在StackOverflow上得到的建议。
每个任务大约需要30分钟,但可以很容易地并行处理。为了这个原因,我使用Pool.map
来拆分任务并并行处理。在命令行上运行时效果很好,使用新型多核芯片时,运行时间大约在5分钟左右。
不幸的是,有一些限制不允许守护进程(daemonic process)有子进程,当我在CGI队列中运行这个复杂的并行代码时,出现了这个错误:
AssertionError: 不允许守护进程有子进程
我注意到其他人也有类似的问题,但我找不到一个不需要完全放弃Pool.map
,而且不需要写更复杂的线程代码的答案。
在这种情况下,合适的设计选择是什么呢?我可以很容易地通过Celery队列运行我的串行任务。我也可以在没有队列的情况下运行更快的并行任务。我该如何处理这个问题?是否有可能同时实现我想要的(队列和每个任务的并行处理)?
我想到了一些主意(有些可能有点hacky):
- 将任务发送到Celery队列时,直接调用命令行程序。这个程序可以随意使用Pool,然后将结果和数据保存到文件中(就像现在一样)。
缺点:我无法检查任务的状态或确认它是否成功结束。此外,从CGI发起的系统调用可能会引发安全问题。 - 显然,如果队列里任务很多,我可以利用CPU资源(通过设置CELERYD_CONCURRENCY = 6左右);这样可以让很多人同时“在队列前面”。
缺点:每个任务在队列前面会花费很多时间;如果队列不满,就不会加快速度。此外,很多部分完成的任务会同时存储在内存中,使用更多的RAM。 - 使用Celery的@task在子任务中进行并行处理。然后,我可以将CELERYD_CONCURRENCY设置为6(或我希望同时在内存中允许的子任务数量)。
缺点:首先,我不确定这是否能成功避免“任务中的任务”问题。而且,队列位置的概念可能会丢失,很多部分完成的任务可能会同时出现在内存中。 - 也许有办法调用Pool.map并指定线程为非守护进程?或者有没有更轻量级的东西可以替代Pool.map?这类似于另一个StackOverflow问题中的方法。此外,我应该指出,通过Pool.map实现的并行处理类似于线性代数,没有进程间通信(每个任务独立运行并返回结果,而不与其他任务交流)。
- 放弃Celery,使用multiprocessing.Queue。这样也许可以让每个线程使用相同的“线程深度”(即所有线程都可以使用相同的Pool,避免嵌套)?
非常感谢!
2 个回答
我正在使用基于Twisted的多进程守护进程,这些守护进程可以正常处理分叉和Gearman的任务查询。
可以去了解一下Gearman。
你需要的是一个工作流程管理系统(WFMS),它可以管理以下内容:
- 任务并发
- 任务依赖
- 任务嵌套
还有其他一些功能。
从一个很高的角度来看,WFMS就像是一个在任务池(比如celery)上面的管理者,它会把准备好执行的任务提交到这个池子里。同时,它还负责打开一个嵌套的任务,并相应地提交里面的任务。
我开发了一个这样的系统,叫做 pomsets。你可以试试看,如果有任何问题,随时问我。