工作中的 Sidekiq 重试计数

Posted

技术标签:

【中文标题】工作中的 Sidekiq 重试计数【英文标题】:Sidekiq retry count in job 【发布时间】:2014-05-28 17:40:56 【问题描述】:

有没有办法获取当前作业的重试次数?

我希望作业在重试 x 次后停止,而不是崩溃。我想问一下perform方法中的重试次数,如果重试次数等于x,我可以简单地返回。

def perform(args)
  return if retry_count > 5
  ...
end

使用 Sidekiq 2.12。

编辑

我(不是 OP)有同样的问题,但出于不同的原因。如果正在重试该作业,我想进行额外的完整性检查以确保需要该作业,并在由于排队后发生外部更改而不再期望该作业成功时退出重试。

那么,有没有办法获取当前作业的重试次数?当前的答案只建议了您可以绕过需要它或可以从工作之外获得它的方法。

【问题讨论】:

你有什么解决办法吗? 【参考方案1】:

这可以通过添加一个 sidekiq 中间件来将 msg['retry_count'] 设置为作业类的实例变量来实现。

添加一个中间件(在 Rails 中,通常是/config/initializers/ 文件夹中的一个文件),如下所示:

class SidekiqMiddleware
    def call(worker, job, queue)
        worker.retry_count = job['retry_count'] if worker.respond_to?(:retry_count=)
        yield
    end
end

Sidekiq.configure_server do |config|
    config.server_middleware do |chain|
        chain.add SidekiqMiddleware
    end
end

在你的工作中:

include Sidekiq::Worker
attr_accessor :retry_count

def retry_count
  @retry_count || 0
end

def perform(args)
  return if retry_count > 5
  ...
end

【讨论】:

中间件类放哪个文件夹?我可以把它放在我放“Sidekiq.configure_server”的地方吗? 为什么我会收到这个错误 NoMethodError: undefined method retry_count=' 对于 #<:extensions::delayedmailer:0x007f9ad8928338>` 其实这个方法虽然有效,但是retry_count阅读器的代码是不正确的,因为它会依次返回0、0、1、2、3、4、5、6、7 , 8, 9, 10. 双 0 的原因是,在中间件级别,:retry_count 键在第一次执行时不存在,并且在第一次重试时添加键时(也就是第二次执行作业)它的值是0(不是此代码假定的1)。 返回nil, 0, 1, 2, 3, 4, ... 所以if msg['retry_count'].nil? then retry_count = 0 else retry_count = msg['retry_count'] + 1 end 这个答案最初检查了respond_to?(:retry_count),但我改为respond_to?(:retry_count=),因为这是它实际使用的方法。这可能是@MaxRose-Collins 获得NoMethodError 的部分原因【参考方案2】:

你不需要直接处理这个逻辑来完成你想要的。只需向您的工作人员添加一些配置即可..注意 sidekiq_options 。根据您在下方的评论“阻止 Sidekiq 将作业移至死作业队列”

 class MyWorker  
     include Sidekiq::Worker
     sidekiq_options :retry => 5, :dead => false

      def perform
          #do some stuff
      end
 end

那么作业应该重试 5 次并优雅地失败。此外,如果您想在重试 5 次后执行代码块,worker 有一个名为 sidekiq_retries_exhausted 的方法,您可以在其中进行一些自定义日志记录等。

【讨论】:

谢谢,但是在 5 次尝试之后,这将作为 失败 工作而结束,我想尝试 5 次,如果它不起作用,请停止而不引发错误。那不一样。 sidekiq_retries_exhausted 是同样的事情,已经太晚了,工作失败。我想在它失败之前停止它 也许我可以使用sidekiq_retries_exhausted 来防止 Sidekiq 将作业移动到死作业队列? 哦,原来这在 Sidekiq 3.0 中真的很简单,答案更新了 太好了,谢谢。下周将升级 Sidekiq 并报告! 文档链接:github.com/mperham/sidekiq/wiki/Error-Handling【参考方案3】:

您可以使用 Sidekiq API 访问重试:

https://github.com/mperham/sidekiq/wiki/API#retries

找到您需要的工作并使用job['retry_count'] 获取重试次数。

【讨论】:

谢谢,但可以从 within 作业 while 访问它正在运行的重试。该作业当时不再在 RetrySet 中,因为它是活动的,正在运行的。【参考方案4】:

我的用例是避免在部署期间发生异常/停机时安排多个作业。为此,我需要 retry_count。以上解决方案不适用于sidekiq ~> 5.0.4,这是我测试过的解决方案

# config/initializers/sidekiq.rb

# define your middleware
module Sidekiq::Middleware::Server
  class SetRetryCountMiddleware
    def call(worker, job_params, _queue)
      retry_count = job_params["retry_count"]
      worker.instance_variable_set(:@retry_count, retry_count)
      yield
    end
  end
end

# add your defined middleware
Sidekiq.configure_server do |config|
  config.server_middleware do |chain|
    chain.add Sidekiq::Middleware::Server::SetRetryCountMiddleware
  end
  config.redis = url: "redis://sidekiq:6379/0"
  config.logger.level = Logger::INFO
end

& 在你的工人中

class YetAnotherWorker < Base
  sidekiq_options  :queue => :critical, :retry => true

  def perform(args)
    begin
      # lines that might result in exception
    rescue => exception
      logger.warn("#exception.class")
      raise(exception)
    ensure
      # below line will ensure job is scheduled only once, avoiding multiple jobs if above lines throws an error
      schedule_next_run(my_key: "my_value")
    end
  end

  def schedule_next_run(args)
    YetAnotherWorker.perform_at(Time.now + 7.days, args) if first_run
  end

  def first_run
    @retry_count.nil?
  end

end

此外,retry_count 密钥在首次运行时在 job_params 中不可用,因此计数看起来像 nil,0,1,2..

【讨论】:

以上是关于工作中的 Sidekiq 重试计数的主要内容,如果未能解决你的问题,请参考以下文章

使用 ActiveJob 禁用自动重试,与 Sidekiq 一起使用

如何在sidekiq中设置全局重试限制?

如何使用 ActiveJob 设置 Sidekiq 的重试次数?

部署Cloud66和DigitalOcean的Rails不会影响当前正在运行的sidekiq进程

如果映射器在中途失败并且 Hadoop 重试该映射器,自定义计数器会发生啥

Jmeter:使用 While Controller 进行失败的请求重试,ThreadGroup 循环计数不起作用