在Celery工作进程中捕获Heroku SIGTERM,以优雅地关闭工作进程。

22

我在这个问题上做了很多研究,但令我惊讶的是,在任何地方我都没有找到一个好的答案。

我在Heroku上运行一个大型应用程序,并且有一些长时间运行的celery任务,任务处理完成后会保存结果。每次我在Heroku上重新部署时,它都会发送SIGTERM(最终发送SIGKILL)并杀死我的正在运行的worker。 我正在寻找一种方法让worker实例优雅地关闭自己并重新排队以后进行处理,这样我们最终可以保存所需的结果,而不是丢失排队的任务。

我无法找到一种有效的方法使worker正确监听SIGTERM。到目前为止,我最接近的方式是直接运行python manage.py celeryd时有效,但用foreman模拟Heroku时无效,代码如下:

@app.task(bind=True, max_retries=1)
def slow(self, x):
    try:
        for x in range(100):
            print 'x: ' + unicode(x)
            time.sleep(10)
    except exceptions.MaxRetriesExceededError:
        logger.error('whoa')
    except (exceptions.WorkerShutdown, exceptions.WorkerTerminate) as exc:
        logger.error(u'retrying, ' + unicode(exc))
        raise self.retry(exc=exc, countdown=10)
    except (KeyboardInterrupt, SystemExit) as exc:
        print 'retrying'
        raise self.retry(exc=exc, countdown=10)
    else:
        return x
    finally:
        logger.info('task ended!')

我在Foreman内启动celery任务,并按下Ctrl+C时,会出现以下情况:

^CSIGINT received
22:20:59 system   | sending SIGTERM to all processes
22:20:59 web.1    | exited with code 0
22:21:04 system   | sending SIGKILL to all processes
Killed: 9

很明显, celery 的任何异常、KeyboardInterruptSystemExit 异常(前面的帖子中提到过)都不能正确捕获 SIGTERM 并关闭 worker。

那么该如何正确地做呢?


http://celery.readthedocs.org/en/latest/userguide/workers.html?highlight=sigkill#process-signals 似乎表明主要的工作进程将始终拦截 SIGTERM 信号。 - Raghuram Onti Srinivasan
那么,有没有办法让主要的工作进程将其传播给子进程呢? - jdotjdot
这是一个问题,我也从未找到过一个很好的解决方案。我倾向于在应用程序逻辑中处理它,通过确保我的任务是幂等的,并跟踪已启动和已完成的任务,以便在我的应用程序启动时自动重新启动给定的任务。 - Brock Haywood
有人已经解决了这个问题吗?我也在尝试寻找一个解决方案 - 在部署之前,我需要正确地停止运行的任务,以便它们可以完全结束或在部署重启后重新调度。 - T.Poe
3个回答

2

1

很遗憾,celery并不是为了做干净的关闭而设计的。 永远。 我是说真的。 celery工作进程会响应SIGTERM,但如果任务未完成,则工作进程将等待完成任务,然后才退出。 在这种情况下,如果工作人员在合理的时间内没有关闭,您可以发送SIGKILL,但在这种情况下将会丢失信息,即您可能不知道哪些作业未完成。


由于您可以持久化任务结果,因此在应用程序层检查任务状态等并恢复情况应该是可行的。 - Dwight Gunning

0

您可以使用acks_latetask_acks_late

任务将在执行完成后从队列中确认,而不是在之前。因此,如果工作程序正常关闭,则任务将重新生成。


只是出于好奇,代理(比如..rabbitmq)如何区分未确认的消息(任务),因为: a)任务仍在处理中 b)工作进程已死亡,应重新传递 - Marcos Modenesi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接