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)
}
}
})