Celery:运行冗长初始化函数的正确方法(每个进程)

时间:2014-06-13 10:06:11

标签: python initialization multiprocessing celery python-c-extension

TLDR;

要为celery生成的每个进程运行初始化函数,可以使用worker_process_init信号。正如您可以在docs中读到的那样,该信号的处理程序不应该阻塞超过4秒。 但是有什么选择,如果我必须运行一个执行时间超过4秒的init函数?

问题

我使用C扩展模块在芹菜任务中运行某些操作。此模块需要初始化,可能需要几秒钟(可能是4-10)。因为我宁愿不为每个任务运行这个init函数,但对于每个生成的进程,我都使用了worker_process_init信号:

#lib.py 
import isclient #c extension module
client = None
def init():
    global client
    client = isclient.Client() #this might take a while

def create_ne_list(text):
    return client.ne_receiventities4datachunk(text)

#celery.py
from celery import Celery
from celery.signals import worker_process_init
from lib import init

celery = Celery(include=[
    'isc.ne.tasks'
])

celery.config_from_object('celeryconfig')

@worker_process_init.connect
def process_init(sender=None, conf=None, **kwargs):
    init()

if __name__ == '__main__':
    celery.start()

#tasks.py
from celery import celery
from lib import create_ne_list as cnl

@celery.task(time_limit=1200)
def create_ne_list(text):
    return cnl(text)

当我运行此代码时,我在前面的问题(Celery: stuck in infinitly repeating timeouts (Timed out waiting for UP message))中描述了什么。简而言之:由于我的init函数需要超过4秒,因此有时会发生工作人员被杀死并重新启动,并且在重启过程中会再次被杀死,因为这是4秒无响应后自动发生的事情。这最终导致无限重复的杀戮和重启过程。

另一种选择是使用信号worker_init为每个工作人员仅运行一次init函数。如果我这样做,我会遇到一个不同的问题:现在排队的进程由于某种原因而卡住了。 当我以3的并发性启动工作者,然后发送几个任务时,前三个将完成,剩下的任务不会被触及。 (我假设它可能与事实有关,client对象需要在多个进程之间共享,并且由于某些原因,C扩展不支持它。但说实话,我对muli处理相对较新,所以我可以猜测)

问题

所以,问题仍然存在:如何为每个进程运行一个超过4秒的init函数?有没有一种正确的方法可以做到这一点,那会是什么样的方式?

2 个答案:

答案 0 :(得分:7)

Celery将init超时限制为4.0秒。 查看source code

要解决此限制,您可以考虑在创建芹菜应用之前对其进行更改

from celery.concurrency import asynpool
asynpool.PROC_ALIVE_TIMEOUT = 10.0 #set this long enough

请注意,没有用于更改此值的配置或设置。

答案 1 :(得分:0)

@changhwananswer 不再是 celery 4.4.0 的唯一方法。这是为此功能添加配置选项的 pull request

使用配置选项

对于 celery ^4.4.0,这个值是可配置的。使用 celery 应用程序配置选项 worker_proc_alive_timeout。来自stable version docs

<块引用>

worker_proc_alive_timeout

默认值:4.0。

等待新工作进程启动时的超时时间(整数/浮点数)。

示例:

from celery import Celery
from celery.signals import worker_process_init

app = Celery('app')
app.conf.worker_proc_alive_timeout = 10

@worker_process_init.connect
def long_init_function(*args, **kwargs):
   import time
   time.sleep(8)