如何在不延迟任务的情况下优雅地重启 Celery
Posted
技术标签:
【中文标题】如何在不延迟任务的情况下优雅地重启 Celery【英文标题】:How to restart Celery gracefully without delaying tasks 【发布时间】:2012-03-27 09:53:40 【问题描述】:我们使用 Celery 和 Django webapp 来管理离线任务;其中一些任务可以运行长达 120 秒。
每当我们进行任何代码修改时,我们都需要重新启动 Celery 以让它重新加载新的 Python 代码。我们当前的解决方案是向 Celery 主进程 (kill -s 15 `cat /var/run/celeryd.pid`
) 发送一个 SIGTERM,然后等待它死亡并重新启动它 (python manage.py celeryd --pidfile=/var/run/celeryd.pid [...]
)。
由于长时间运行的任务,这通常意味着关闭将需要一两分钟,在此期间不会处理任何新任务,从而对当前在站点上的用户造成明显的延迟。我正在寻找一种方法来告诉 Celery 关闭,然后立即启动一个新的 Celery 实例以开始运行新任务。
不起作用的事情:
向主进程发送 SIGHUP:这导致 Celery 尝试通过热关机然后重新启动自身来“重新启动”。这不仅需要很长时间,甚至不起作用,因为显然新进程在旧进程死亡之前启动,所以新进程抱怨ERROR: Pidfile (/var/run/celeryd.pid) already exists. Seems we're already running? (PID: 13214)
并立即死亡。 (这看起来像是 Celery 本身的一个错误;我已经 let them know 关于它。)
向主进程发送 SIGTERM,然后立即启动一个新实例:与 Pidfile 相同的问题。
完全禁用 Pidfile:没有它,我们无法确定 30 个 Celery 进程中的哪一个是当我们希望它进行热关闭时需要发送 SIGTERM 的主进程。我们也没有可靠的方法来检查主进程是否还活着。
【问题讨论】:
也许我对***.com/questions/9764913/…的回答对你有帮助。 【参考方案1】:我想你可以试试这个:
kill -s HUP ``cat /var/run/celeryd.pid``
python manage.py celeryd --pidfile=/var/run/celeryd.pid
HUP
可以回收每个空闲的工人,让执行工人继续运行,HUP
会让这些工人得到信任。然后你可以安全地重新启动一个新的 celery worker 主进程和 worker。任务完成后,老工人可能会被杀死。
我在我们的生产中使用过这种方式,现在看起来很安全。希望对您有所帮助!
【讨论】:
【参考方案2】:有点晚了,但可以通过删除名为 celerybeat.pid 的文件来解决。
为我工作。
【讨论】:
【参考方案3】:我最近用 SIGHUP 修复了这个错误:https://github.com/celery/celery/pull/662
【讨论】:
谢谢!但是,您的修复并没有改变 SIGHUP 在终止和重新启动之前等待所有任务完成的事实,这再次导致我试图避免的延迟。关于如何利用您的修复并使其无需等待即可重新启动的想法会很棒...... 这就是我解决问题的方法。我将每个长时间运行的任务(视频转换、电子邮件发送)放在一个单独的队列中,由单独的工作人员处理。因此,当我向所有工作人员发送 SIGHUP 时,我知道来自默认队列的工作人员处理任务不会长时间阻塞,因为只有小任务。视频转换不会阻止小任务。只有视频转换队列被阻塞了一段时间。但这在我的情况下是可以接受的。 所以经过一些测试,我发现您的修复也修复了 SIGTERM 问题。因此,我终于设法通过合并您的修复程序并使用以下命令重新启动 Celery,一劳永逸地解决了这个问题:kill -s SIGTERM ``cat /var/run/celeryd.pid`` && python manage.py celeryd --pidfile=/var/run/celeryd.pid [...]
如果您可以将其放入您的答案中,我会接受!
我认为这是不可靠的行为。我的补丁有一个小错误——它过早地释放了一个 pidlock(在所有任务完成之前)。因此,它允许在旧进程完全关闭之前启动新进程。这是完全不可靠的。当合并到主分支时,这是固定的。您所说的 SIGTERM 错误并不是真正的错误。这只是每个守护进程的正常行为。所以我强烈建议不要利用补丁中的错误,而是使用固定版本:github.com/ask/celery/commit/…【参考方案4】:
您使用 SIGHUP (1) 来热关闭 celery。我不确定它是否真的会导致热关机。但是 SIGINT (2) 会导致热关机。尝试使用 SIGINT 代替 SIGHUP,然后在您的脚本中手动启动 celery(我猜)。
【讨论】:
【参考方案5】:rm *.pyc
这会导致重新加载更新的任务。我最近发现了这个技巧,我只是希望没有讨厌的副作用。
【讨论】:
【参考方案6】:celeryd 有 --autoreload 选项。如果启用,celery worker(主进程)将检测 celery 模块的变化并重新启动所有工作进程。与 SIGHUP 信号相反,autoreload 在当前执行任务完成时独立地重新启动每个进程。这意味着当一个工作进程重新启动时,其余进程可以执行任务。
http://celery.readthedocs.org/en/latest/userguide/workers.html#autoreloading
【讨论】:
虽然--autoreload
被标记为不建议进行实时部署。
在生产中它可以与自定义重新加载器一起使用indelible.org/ink/python-reloading【参考方案7】:
您可以使用自定义 pid 文件名启动它吗?可能带有时间戳,并关闭它以知道要杀死哪个 PID?
CELERYD_PID_FILE="/var/run/celery/%n_timestamp.pid"
^我不知道时间戳语法,但也许你知道或者你能找到它?
然后使用当前系统时间来杀死任何旧的 pid 并启动一个新的?
【讨论】:
我怀疑您需要将问题中的一种技术与此结合起来。根据您的代理,您应该能够使用基于时间戳的 pidfile(使用--pidfile=
)启动一个新的 celery,然后将SIGTERM
发送到所有其他正在运行的 celery 进程以使它们热关机(尽管请注意真的应该只有一个,除非你在旧 celeryd 仍在热关机时尝试这个)。以上是关于如何在不延迟任务的情况下优雅地重启 Celery的主要内容,如果未能解决你的问题,请参考以下文章