Rails 获取 Sidekiq 任务进度

总有一些时候需要获取任务的进度,比如批量导入时,
首先我们需要在 Gemfile 里添加

gem 'sidekiq'
gem 'sidekiq-status'
gem 'sidekiq-throttled'
gem 'sidekiq-unique-jobs'

然后运行 bundle install,等待运行完毕。
为了方便管理,可以在 bin 目录下添加 sidekiq 文件,内容为:

require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

bundle_binstub = File.expand_path("../bundle", __FILE__)

if File.file?(bundle_binstub)
  if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
    load(bundle_binstub)
  else
    abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
  end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("sidekiq", "sidekiq")

现在重启一下服务器,以及启动 sidekiq,(bin/rails s & bin/sidekiq)

这时候就可以写一些异步任务了,那么正常来说,我们需要在 app/ 下新建 'workers' 目录,然后在 'workers' 目录下新建 worker 文件,例如
batch_generate_worker.rb:

class BatchGenerateWorker
  include Sidekiq::Worker
  include Sidekiq::Status::Worker
  sidekiq_options retry: false

  def perform(options)
    write you code
  end
end

其中我们引入了include Sidekiq::Status::Worker,我们就可以通过它来获取进度、状态等等,可以先去看看官方写的文档用法(指路链接
在 perform 里使用 total(100) 来传入总量有多少,使用 at(1) 来传入现在进行到多少个了。例如:

# options = { names: ['aa', 'bb', 'cc'] }
class BatchGenerateWorker
  include Sidekiq::Worker
  include Sidekiq::Status::Worker
  sidekiq_options retry: false

  def perform(options)
    total(options[:names].count)
    options[:names].each_with_index do |name, i|
      User.create!(name: name)
      at(i)
    end
  # 如果中间有错误需要抛出
  rescue => e
    store message: e.message
    logger.error(e.message)
    e.backtrace.each { |msg| logger.error(msg) }
  end
end

我们还需要一个 controller 来获取进度,一般都是通过轮询来获取,so,在 app/controllers 目录下新建一个 jobs_controller.rb
调用这个 BatchGenerateWorker 时,会返回一个 jid,我们可以通过 这个 jid 来查询进度。
jobs_controller.rb:

class JobsController < ApplicationController
  // 看自己需求是否需要 skip 掉
  skip_before_action :authenticate_user!, only: [:show]

  def show
    data = Sidekiq::Status.get_all(params[:jid]) || {}
    payload = data['payload'] ? JSON.parse(data['payload']) : {}
    render_ajax_success(status: data['status'], total: data['total'], at: data['at'], message: data['message'], payload: payload)
  end
end

前端一个 ajax 轮询请求来查就可以了了
简单写一下 js 吧:

let pollResult = (jid) => {
  $.ajax({
    url: '/jobs/' + jid,
    success: (data) => {
      progress = Math.round((data.at / data.total) * 10000) / 100.0
      if (data.status == 'queued' || data.status == 'working' || data.status == 'retrying') {
        setTimeout(() => {
          pollResult(jid)
        }, 1000)
      } else {
      }
  })
}

$.ajax({
  type: 'xxx',
  url: 'xxxx',
  success: function (data) {
    if (data.success) {
      pollResult(data.jid)
    }
  }
})
posted @ 2021-07-11 16:37  Mr-Ran  阅读(258)  评论(0编辑  收藏  举报