如何获取Sidekiq队列中指定任务的当前位置?
获取Sidekiq队列中指定任务的位置
好问题!Sidekiq官方文档里确实没有直接提供获取任务在队列中精确位置的API,但我们可以通过几种变通方法来实现这个需求,适配你用2个Worker处理默认队列的场景:
方法一:扫描队列统计位置(简单直接)
提交任务时,先记录下任务的jid(Sidekiq自动生成的唯一ID),之后定期扫描队列,统计目标任务之前的任务数量就能得到位置。
示例代码:
提交任务时保存ID:
# 提交任务并获取唯一ID job_jid = YourWorker.perform_async(your_args) # 可以把这个jid关联到你的业务记录(比如存到数据库或Redis),方便后续查询
查询任务位置的方法:
def get_job_position_in_queue(jid, queue_name = "default") target_queue = Sidekiq::Queue.new(queue_name) current_position = 0 target_queue.each do |job| break if job.jid == jid current_position += 1 end # 检查任务是否还在队列中 job_exists = target_queue.any? { |job| job.jid == jid } job_exists ? current_position : "任务已执行完成或不存在" end
注意:
- 这个方法的缺点是队列越大,扫描速度越慢,如果你的队列经常有数百个任务,偶尔查询一次没问题,但频繁调用可能会影响Redis性能。
- 如果用了Sidekiq Pro的优先级队列,任务顺序会受优先级影响,扫描时要注意优先级高的任务会排在前面,统计位置时要考虑这一点。
方法二:用Redis有序集合自定义追踪队列(高效查询)
如果需要频繁查询任务位置,可以基于Redis的ZSet(有序集合)来封装自己的队列逻辑,把任务的提交时间或优先级作为排序分数,这样就能用Redis的ZRANK命令直接获取任务位置,性能比扫描队列好很多。
示例代码:
自定义入队方法:
def enqueue_with_tracking(worker_class, args) job_jid = SecureRandom.uuid # 用时间戳作为分数,保证先进先出;如果需要优先级,可以把优先级和时间结合成分数 sort_score = Time.now.to_f Sidekiq.redis do |redis| job_payload = { jid: job_jid, class: worker_class.to_s, args: args }.to_json redis.zadd("tracked_default_queue", sort_score, job_payload) end # 同时还要把任务放到Sidekiq原生队列,让Worker能处理 worker_class.perform_async(args) job_jid end
查询位置的方法:
def get_job_position_fast(jid) Sidekiq.redis do |redis| # 遍历ZSet找到目标任务的rank(注意ZRANK返回的是0开始的索引) rank = redis.zscan_each("tracked_default_queue").find do |payload, _| JSON.parse(payload)["jid"] == jid end&.then { |payload, _| redis.zrank("tracked_default_queue", payload) } rank ? rank + 1 : "任务已执行完成或不存在" end end
注意:
- 这个方法需要同步维护自定义ZSet和Sidekiq原生队列,任务执行完成后要记得从ZSet中移除,避免数据冗余。
- 如果你需要Sidekiq的重试、死信队列等功能,还要额外处理这些场景下的ZSet更新逻辑。
方法三:估算等待时间(无需精确位置)
如果不需要精确位置,只是想知道任务大概还要等多久,可以通过统计历史数据来估算:
- 记录每个Worker处理任务的平均耗时
- 用当前队列长度乘以平均耗时,再结合任务提交的时间,就能算出大致的等待时间
这种方法不需要扫描队列,性能最好,适合只需要大致预期的场景。
内容的提问来源于stack exchange,提问作者stratis
相关产品推荐
相关产品推荐

