You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 09:15:02