【问题标题】:Recovering cleanly from Resque::TermException or SIGTERM on Heroku从 Heroku 上的 Resque::TermException 或 SIGTERM 干净地恢复
【发布时间】:2013-02-11 01:03:38
【问题描述】:

当我们重新启动或部署时,我们会在失败的队列中使用 Resque::TermException (SIGTERM)Resque::DirtyExit 获得许多 Resque 作业。

我们在 Procfile 中使用了新的TERM_CHILD=1 RESQUE_TERM_TIMEOUT=10,所以我们的工作行看起来像:

worker:  TERM_CHILD=1 RESQUE_TERM_TIMEOUT=10 bundle exec rake environment resque:work QUEUE=critical,high,low

我们还使用了resque-retry,我认为这可能会自动重试这两个异常?但好像不是。

所以我猜有两个问题:

  1. 我们可以在每个作业中从Resque::TermException 手动救援,并使用它来重新安排作业。但是有没有一种干净的方法可以为所有工作做到这一点?甚至是猴子补丁。
  2. 不应该 resque-retry 自动重试这些吗?你能想出为什么不这样的原因吗?

谢谢!

编辑:在 10 秒内完成所有工作在规模上似乎是不合理的。似乎需要一种方法来在运行 Resque::DirtyExit 异常时自动重新排队这些作业。

【问题讨论】:

    标签: heroku resque resque-retry


    【解决方案1】:

    我也遇到了这个问题。事实证明,Heroku 不仅向父进程发送SIGTERM 信号,而且向所有分叉进程发送信号。这不是 Resque 期望的逻辑,它会导致 RESQUE_PRE_SHUTDOWN_TIMEOUT 被跳过,从而强制执行作业而没有任何时间尝试完成作业。

    在发出SIGTERM 后,Heroku 给工作人员 30 秒的时间来正常关闭。在大多数情况下,这是足够的时间来完成作业,如果作业无法完成,则剩余一些缓冲时间可以将作业重新排队到 Resque。但是,要使用所有这些时间,您需要设置 RESQUE_PRE_SHUTDOWN_TIMEOUTRESQUE_TERM_TIMEOUT 环境变量以及修补 Resque 以正确响应 SIGTERM 被发送到分叉进程。

    这是一个修补 resque 并更详细地解释此问题的 gem:

    https://github.com/iloveitaly/resque-heroku-signals

    【讨论】:

    • 这是正确的解释。谢谢@iloveitaly
    • 我很好奇是否有人了解 resque 2.0.0 的当前情况。我不相信最近 resque 的旧修补 gem,但也不确定 resque 是否正常运行。
    • resque-heroku-signals gem 适用于 resque 2.0。我在生产中使用它。
    【解决方案2】:

    您的 resque 作业是否需要超过 10 秒才能完成?如果在发送初始 SIGTERM 后 10 秒内完成作业,您应该没问题。尝试将作业分解成更快完成的小块。

    此外,您可以让您的工作人员重新排队执行以下操作:https://gist.github.com/mrrooijen/3719427

    【讨论】:

    • 赞成并接受 - 老实说,我不确定我们是否可以在 10 秒内完成所有操作。我们有一些需要生成一个文件的大型导出等。重新入队似乎可以解决这个问题?你能分享Resque::TermExceptionResque::DirtyExit之间的区别吗?我在那里为Resque::DirtyExit 提供了救援,但它似乎并不总是重新排队。谢谢!
    • 作为更新,奇怪的是,尽管有 rescue Resque::DirtyExit 在工作中,但有时他们并没有干净利落地挽救这些异常。我一直无法弄清楚为什么。这使我们的工作变得不可靠,因为我们仍然在带有 Resque::DirtyExit 异常的失败队列中找到它们。这真的成​​为一个问题
    • 有人可以推荐工人应该如何处理工人内部的 SIGTERM 以便工人可以干净地关闭自己吗?例如,(resque)工作人员是否也应该捕获 SIGTERM 并设置循环代码定期检查的一些变量?我假设 TermException 或 DirtyException 仅在 RESQUE_TERM_TIMEOUT 秒后才受到限制。
    【解决方案3】:

    我也为此苦苦挣扎了一段时间,但没有找到可靠的解决方案。

    我发现的少数解决方案之一是按计划运行 rake 任务(每 1 分钟一次的 cron 作业),它查找因 Resque::DirtyExit 失败的作业,重试这些特定作业并从失败队列中删除这些作业.

    这是 rake 任务的示例 https://gist.github.com/CharlesP/1818418754aec03403b3

    这个解决方案显然不是最理想的,但迄今为止,它是我找到的重试这些工作的最佳解决方案。

    【讨论】:

      【解决方案4】:
      1. 我们可以在每个作业中手动从 Resque::TermException 中救援,并使用它来重新安排作业。但是有没有一种干净的方法可以做 这适用于所有工作?甚至是猴子补丁。

      当使用SIGTERM 信号终止作业时,会引发Resque::DirtyExit 异常。该作业没有机会像read here 那样捕获异常。

      1. 不应该 resque-retry 自动重试这些吗?你能想出为什么不这样的原因吗?

      不明白为什么不应该,调度程序是否正在运行?如果不是rake resque:scheduler

      我写了一篇详细的博客文章,围绕我最近使用Resque::DirtyExit 遇到的一些问题,也许有用=> Understanding the Resque internals – Resque::DirtyExit unveiled

      【讨论】:

      • 您提到了SIGTERM,但链接到了SIGKILL。链接特别说SIGTERM可以被拦截。
      猜你喜欢
      • 1970-01-01
      • 2016-12-20
      • 1970-01-01
      • 2011-06-21
      • 2014-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多