我不认为有一个很好的答案,因为如果我记得正确的 Sidekiq 队列使用 Redis 列表,那么就有 FIFO 的期望。重试的作业会在同一个队列中排队,这意味着它们将始终排在最后。
一种不太好且不是我推荐的方法是添加另一个队列并将作业重试发送给它:
# config/sidekiq.yml
---
:queues:
- default
- my_worker_retries
设置worker不重试:
class MyWorker
include Sidekiq::Worker
sidekiq_options retry: false
end
确保您的工作人员可预见地引发错误,如下所示:
class MyWorker
include Sidekiq::Worker
sidekiq_options retry: false
def perform(arg)
raise ArgumentError
end
end
添加一些逻辑来处理该异常,然后通过新创建的队列再次运行此作业:
class MyWorker
include Sidekiq::Worker
sidekiq_options retry: false
def perform(arg)
begin
raise ArgumentError
rescue ArgumentError => error
MyWorker.set(queue: :my_worker_retries).perform_async(arg)
end
end
end
这意味着任何失败并在 my_worker_retries 队列中排队的作业都可能陷入无限循环 - 作业失败,获救,排队,再次失败 - 更糟糕的是,因为你不是利用 Sidekiq 内置的重试队列机制,没有退避算法来确保重试不会以您的 CPU 可以处理的速度触发。
整个东西都很脆弱。
您可以尝试通过传递一个指示此作业已重试多少次的参数来防止这种情况发生,这样您就可以在某个次数后停止:
class MyWorker
include Sidekiq::Worker
sidekiq_options retry: false
MAX_RETRIES = 5
def perform(arg, retries = 0)
raise 'Too many retries' if retries >= MAX_RETRIES
begin
raise ArgumentError
rescue ArgumentError => error
MyWorker.set(queue: :my_worker_retries).perform_async(arg, retries + 1)
end
end
end
您可以扩展它以拥有自己的退避算法:
MyWorker.set(queue: :my_worker_retries).perform_in((retries + 1).hours, arg, retries + 1)
这些都不是理想的,但它确实回答了这个问题。我当然希望有比这更好的解决方案。
有一些 Sidekiq 扩展可能会起作用,例如 https://github.com/chartmogul/sidekiq-priority_queue,但我之前没有使用过。