Rails中如何让Sidekiq任务在前一个执行完后间隔2秒依次执行
实现方案
方案1:使用Sidekiq Enterprise序列特性(商业版)
Sidekiq商业版原生支持顺序执行任务的特性,无需额外开发,直接配置即可使用:
- 在Job类中添加序列配置:
class MyAwesomeJob < ApplicationJob sidekiq_options sequence: :my_awesome_job_queue, sequence_ttl: 1.hour end
- 入队逻辑无需修改,还是调用
MyAwesomeJob.perform_in(2.seconds, stuff),同一个sequence分组下的任务会自动按照入队顺序执行,前一个任务执行完成后才会启动下一个,完全符合需求,唯一要求是需要购买Sidekiq Enterprise商业授权。
方案2:自定义实现(开源无额外依赖)
不需要商业授权,通过Redis维护待执行队列+运行锁实现,代码如下:
class MyAwesomeJob < ApplicationJob sidekiq_options retry: 3 # 自定义Redis key,按业务场景隔离 PENDING_QUEUE_KEY = "my_awesome_job:pending_tasks" RUNNING_LOCK_KEY = "my_awesome_job:running_lock" LOCK_TTL = 10.minutes # 锁过期时间,需大于单任务最大执行时长,防止死锁 TASK_INTERVAL = 2.seconds # 任务间隔时间 def perform(passed_stuff = nil) # 无传入参数说明是调度触发,从待执行队列取第一个任务 stuff = if passed_stuff.nil? raw = $redis.lpop(PENDING_QUEUE_KEY) return if raw.blank? # 无待执行任务直接退出 JSON.parse(raw) else passed_stuff end # 原有业务逻辑 process_stuff(stuff) # 执行成功触发下一个任务,不需要失败中断的话可以把这段移到ensure块里 next_raw = $redis.lpop(PENDING_QUEUE_KEY) if next_raw.present? MyAwesomeJob.perform_in(TASK_INTERVAL, JSON.parse(next_raw)) else # 无后续任务释放锁 $redis.del(RUNNING_LOCK_KEY) end rescue => e # 自定义错误处理逻辑,比如重试、告警 raise e # 保留原有的Sidekiq重试逻辑 end # 对外暴露的入队方法,替换原有perform_in调用 def self.enqueue(stuff) # 任务参数加入待执行队列尾部 $redis.rpush(PENDING_QUEUE_KEY, stuff.to_json) # 尝试加锁,加锁成功说明当前无运行中任务,触发第一个任务执行 lock_got = $redis.set(RUNNING_LOCK_KEY, "1", nx: true, ex: LOCK_TTL) MyAwesomeJob.perform_in(TASK_INTERVAL) if lock_got end private def process_stuff(stuff) # 你的原有业务逻辑 end end
使用时把原来的MyAwesomeJob.perform_in(2.seconds, stuff)替换为MyAwesomeJob.enqueue(stuff)即可:1秒内收到10个请求时,只有第一个请求能成功加锁触发第一个任务,剩下9个请求只会把参数写入Redis队列,任务会按入队顺序依次执行,每个任务间隔2秒,完全匹配需求。
方案3:使用第三方开源Gem sidekiq-sequential
不想自己写Redis操作可以使用封装好的开源Gem,步骤如下:
- 先在Gemfile添加依赖:
gem 'sidekiq-sequential',执行bundle install安装 - 给Job添加顺序执行配置:
class MyAwesomeJob < ApplicationJob include Sidekiq::Sequential # 同一个sequential_key下的任务会按入队顺序执行 sidekiq_options sequential_key: :my_awesome_job_group end
- 入队直接调用
MyAwesomeJob.perform_in(2.seconds, stuff)即可,Gem会自动保证顺序执行。
注意事项
- 任务失败逻辑可自行调整:如果要求前序任务执行失败就中断后续任务,保持现有逻辑即可;如果要求失败也继续执行后续任务,把触发下一个任务的逻辑移到
ensure块中即可 - Redis的key要按业务场景做隔离,多个不同任务不要共用相同的key
- 锁的过期时间要设置得比单任务最长执行时间长,避免正常运行的任务被提前释放锁导致重复调度
内容的提问来源于stack exchange,提问作者Fábio BC Souza
相关产品推荐
相关产品推荐

