Sidekiq (8700✨)html
git : https://github.com/mperham/sidekiq mysql
https://www.cnblogs.com/richard1234/p/3829074.html (一篇文章,講的比較清楚)git
https://wdxtub.com/2016/07/06/sidekiq-guide/ (wiki的翻譯)github
進階精華貼:https://ruby-china.org/topics/31470; https://ruby-china.org/topics/36825 web
Sidekiq 是Ruby社區最受歡迎的多線程的異步任務框架之一,幾乎是Rails項目標配。redis
很是優秀的後臺任務處理軟件,其依賴Redis實現隊列任務的增長,重試和調度等。 sql
在Web請求中,有不少任務是能夠放到後臺執行的,好比用戶購買商品付款成功後,就能夠直接向用戶購買成功,相應的短信發送,物流通知等等就能夠放到後臺任務去作,不用在用戶購買的同時當即執行,這些任務也稱做異步任務。數據庫
基本概念: api
Job
緩存
在 Sidekiq 中的 Job 指的是某一個任務的一次執行, 注意與咱們一般意義上說 「一個 Job」 指的是一類 Job.(一類任務)
Worker
由於 Sidekiq
是使用 Celluoid 來完成其多線程的控制的, 而 Celluoid 是 Ruby 中的多線程模式 Actor 模式的實現, 因此在 Sidekiq 中的 Worker 咱們以擬人的方式去理解. 我擁有一個工人, 不過有一點要區分的是這些 Worker 都是按照」操做手冊」在執行任務, 因此他不會被限制在某一類任務上.
Queue
隊列的意義在於區分任務而且讓任務排隊, Sidekiq 中將每一類的任務使用一個 Queue 來區分開.
按照視頻步驟操做失敗。 谷歌中文的帖子,最後成功。
- 根據教材安裝gem 'sidekiq'
- 進行相關配置,下載的rails模版已經在sidekiq.rb中配置,並在routs.rb中加入 monitoring監控。
- rails g sidekiq:worker Hard
- 而後打開在方法perform(參數)內設置如puts("xxx")等等,
- 在控制器action,models中建立一個job。job是指某一個任務的一次執行,一般咱們說一個job🈯️的是一類job。
class HomeController < ApplicationController
def index
HardWorker.perform_async('北京天安門', 5)
end
end
⚠️ :也能夠建立一個將來處理的job:HardWorker.perform_in(1.minutes, '北京王府', 5)
6. 啓動服務器rails server。啓動bundle exec sidekiq 還要啓動redis-server.
7. 打開網頁localhost:3000,及監控localhost:3000/sidekiq
8. 在terminal的bundle(fserver_watch),就是打開sidekig的那個窗,可看到每次處理job時的輸出:
北京天安門: 5
2018-06-25T12:51:36.332Z 8891 TID-ovn7u3uq7 HardWorker JID-93c778e31d4cecfee0e3a3cd INFO: done: 0.022 sec
❌,從rails console中進行操做,提示錯誤 (已解決✅緣由多是沒有rails db:migrate)。
)
> HardWorker.perform_async('bob', 5)
Traceback (most recent call last):
1: from (irb):1
NameError (uninitialized constant HardWorker)
✅返回 => "350788261bbeb33e6a7b187c"
如何訪問 http://localhost:3000/sidekiq 進入監控頁面
(點擊查看Monitoring文檔)
步驟:在routes.rb中設置:
- require 'sidekiq/web'
- 在Rails.application.routes.draw的塊內,添加mount Sidekiq::Web => '/sidekiq'
Rails.application.routes.draw do
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
root to: 'addresses#index'
resources :addresses
end
Sidekiq 管理 UI
在實際網站經營中,只有管理員纔可以看監控,所以須要加上管理員的權限驗證。在第2步:
config/routes.rb
Rails.application.routes.draw do
+ require 'sidekiq/web'
+ authenticate :user, lambda { |u| u.is_admin? } do + mount Sidekiq::Web => '/sidekiq'
+ end
is_admin?是user.rb中的驗證方法。
mount(app, options=nil)是增長一個Rack-based application。例子:
mount(SomeRackApp => "some_route", as: '客製化helper's name')
新增config/sidekiq.yml
The Sidekiq configuration file is a YAML file that Sidekiq server uses to configure itself, by default located at config/sidekiq.yml
.
只有在使用高級功能時才須要建立這個文件,好比並發池such as concurrency pool size, 命名named queues, PID file location, etc.
---
:concurrency: 5 staging: :concurrency: 10 production: :concurrency: 20 :queues: - critical - default - low
Activejob能夠和Sidekiq一塊兒使用
之前的博客: https://www.cnblogs.com/chentianwei/p/9123322.html
操做文檔: https://github.com/mperham/sidekiq/wiki/Active+Job
首先:
而後在config/application.rb中的 Application類中添加:
class Application < Rails::Application
# ...
config.active_job.queue_adapter = :sidekiq
end
或者在config/environments/developments或者 production.rb中添加。
也能夠直接在在每一個job Basis內添加self.active_job = XXX
最後,在任意位置添加job queue.
ExamleJob.perform_later(參數)
(見之前的博客,enqueue, callback )
Activejob補充:
入隊一個等待performed的job,當queueing system空閒的時候,就會當即執行:
CompanyJob.perform_later(參數)
入隊一個在將來某個時間執行的job:
CompanyJob.set(wait_until: Date.tomorrow.noon).perform_later(參數)
當即執行一個job
CompanyJob.perform_now(參數)
set()方法:建立一個預製選項的job。根據選擇中的參數來調用perform_later方法入隊job。
option:
- wait: 指定的延長多少時間後執行入隊job,如wait: 5.minutes
- wait_until: 指定到將來的某個時間點時,入隊job
- priority: 10 入隊這個job並設定優先級
- queue: :some_queue 入隊這個job到指定的隊列queue, 寫隊列名字
例子:
VideoJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later(Video.last)
queue_as :default : 設置隊列的名字,默認在jobs/XXX_job.rb中自動生成,能夠改爲類名。
queue_name: 這個方法會調用隊列的名字,如CompanyJob.queue_name返回:default
Active Job Callbacks (API)
6個鉤子,after, before, around. 針對enqueue和perform
retry_job
ActiveJob::Exceptions中的實例方法。配合rescue_from使用。當從你的job中,營救一個exception,你可讓Active Job再嘗試執行這個job.⚠️和set()方法的option同樣。
客製化 錯誤處理
Activejob不支持豐富的Sidekiq's retry feature.
它使用一個簡單的抽象方式來解析retries,基於遭遇的指定的例外。
⚠️,rails server後,在網頁上出現❌提示pasting
NameError in HomeController#index
uninitialized constant ExampleJob::ErrorLoadingSite
從rescue_frome()中,去掉ErrorLoadingSite參數,則能夠執行。
class ExampleJob < ApplicationJob::base
queue_as :default
rescue_from(ErrorLoadingSite) do
retry_job wait: 5.minutes, queue: :low_priority
end
def perform(args)
puts "#{args} "
end
end
限制
Rails容許對象做爲參數傳入perform方法。
def perform(user)
user.send_welcome_email!
end
可是若是在job 入隊後,perform方法調用以前,user記錄被刪除的狀況下,會升起raise
所以:須要加上rescue_from
rescue_from ActiveJob::DeserializationError do |exception|
# handle a deleted user record
end
✅使用標準的Sidekiq寫法,則是:
def perform(user_id)
user = User.find_by(id: user_id)
if user
user.send_welcome_email!
else
# handle a deleted user record
end
end
Action Mailer(沒看)
Job ID (不知道用處)
ActiveJob有本身的Job ID。Rails5,能夠獲得Sidekiq's JID
> com = CompanyJob.perform_later
Enqueued CompanyJob (Job ID: 0c4eb163-60fc-4b91-a4d5-1098f50c8c65) to Sidekiq(default)
=>
#<CompanyJob:0x00007f858ef6c190 @arguments=[], @job_id="0c4eb163-60fc-4b91-a4d5-1098f50c8c65", @queue_name="default", @priority=nil, @executions=0, @provider_job_id="5c8f31d1e02d6deca5371bfa">
> com.provider_job_id =>"5c8f31d1e02d6deca5371bfa"
provider_job_id就是Sidekiq's JID
❌Activejob沒有perform_in()方法
我不知道如何使用,延時功能。
Sidekiq是一個後臺任務進程的框架。 經過在後臺perform work,它讓你測量你的app。
這須要3部分:
Client
Sidekiq客戶端運行在Ruby應用進程中, 容許咱們建立jobs以後,過一段時間再進行處理。
2個方法來建立一個job:
MyWorker.perform_async(
1,
2,
3)
Sidekiq::Client.push(
'class' => MyWorker,
'args' => [
1,
2,
3])
|
2個方法等價,建立一個Hash表明這個job,序列化Hash爲一個JSON string,並把string入棧(in Redis)。這意味參數只能是簡單的JSON 數據類型,複雜的Ruby對象(Date,Time, ActiveRecord)會出現沒法正常序列化sserialize的問題。
⚠️使用Activejob的話,能夠傳遞記錄對象做爲參數。不過設置上要當心。
Redis
It holds all the job data along with runtime and historical data to power Sidekiq's Web UI. Redis 爲Sidekiq提供數據存儲。
See Using Redis for info about connecting to Redis.(暫時忽略)
Rails5.2提供了Redis Cache Store內建支持(使用哪一個緩存存儲器由使用者決定。點擊看博客
Redis 是一種小型數據結構key-value結構,做爲搭配用的數據庫來使用。咱們在百寶箱用 sidekiq 實做異步時看過它。
Server
每一個Sidekiq服務進程會從在Redis中的隊列拉任務,並處理它們。和你的web進程同樣,Sidekiq boots Rails,這樣全部的jobs和workers就能使用所有的Rails API。服務器將實例化the worker並調用其中的perform方法及其參數。
1.讓參數小而簡單,若是使用Activejob,就能傳遞複雜對象。
2讓job知足事物性。⚠️不明白
3.擁抱併發性。
Sidekiq是併發設計的,能夠同時執行大量jobs.
你可使用鏈接池來限制總共的鏈接數量,若是你的Sidekqi進程佔用太多流量。
Concurrency 併發
https://github.com/mperham/sidekiq/wiki/Advanced-Options#concurrency
默認一個進程建立25個threads。若是crushing你的機器i/o,你能夠調整它:
sidekiq -c 10
另外不要設高於50的threads。在config/database.yml中,設置鏈接pool的數量最好和threads的數量相等。
production:
adapter: mysql2
database: foo_production
pool: 25
提供一些初始設置,給queue換個名字,設置retry的最大次數
- queue : use a named queue for this Worker, default 'default'
- retry : enable the RetryJobs middleware for this Worker, default true. Alternatively, you can specify the max. number of times a job is retried (ie. :retry => 3)
- backtrace : whether to save any error backtrace in the retry payload to display in web UI, can be true, false or an integer number of lines to save, default false. (不太懂)
class HardWorker
include Sidekiq::Worker
sidekiq_options :queue => :crawler, :retry => false, :backtrace => true
def perform(name, count)
end
end
Redis的使用: (點擊看詳細文檔)
Sidekiq 使用 Redis 來保存全部的 job 和操做數據。默認會去鏈接位於 localhost:6379
的 Redis 服務器。可是在生產環境中須要自定義地址。
具體須要修改 config/initializers/sidekiq.rb
文件
若是要使用雲服務器的話:具體看文檔。
數據匯出也是一個經常使用異步來處理的任務,流程和匯入其實87分像:
- 創建 RegistraionExport model,這個 model 會紀錄是那個 user 作匯出、是哪一個 event 要匯出,以及存儲最後匯出的檔案
- 創建一個 RegistrationExports controller,這個 controller 讓用戶能夠新增匯出紀錄,以及瀏覽匯出紀錄
- 創建 ExportWorkerJob,這個異步任務會執行匯出操做,並將匯出的檔案放到 RegistraionExport model 上
- 異步任務最後完成時,能夠寄 E-mail 通知用戶匯出的檔案已經準備好了
最後,匯出和匯入的功能要完整實作的話,還須要考慮檔案存儲的位置。咱們用 carrierwave 上傳的檔案,默認是公開的。可是匯出和匯入的檔案,應該也必需要檢查有沒有權限才行。這部分的實做牽扯到咱們使用哪一種檔案服務器:
報名15分鐘內沒完成自動取消
異步處理能夠設定延遲時間set方法
rails g job check_registration
app/jobs/check_registration_job.rb
class CheckRegistrationJob < ApplicationJob
queue_as :default
+ def perform(registration_id) + registration = Registration.find(registration_id) + + if registration.status != "confirmed" + registration.status = "cancalled" + registration.save! + end
app/controllers/registrations_controller.rb
class RegistrationsController < ApplicationController
before_action :find_event def create @registration = @event.registrations.new(registration_params) @registration.ticket = @event.tickets.find( params[:registration][:ticket_id] ) @registration.status = "pending" @registration.user = current_user @registration.current_step = 1 if @registration.save
flash[:notice] = "報名成功,請在15分鐘內完成操做!" + CheckRegistrationJob.set( wait: 15.minutes ).perform_later(@registration.id)
註冊過程被分解爲3step,第一步是create,此時提示用戶在"15分鐘內完成操做。由於15分鐘後會執行隊列中的CheckRegistrationJob類中的方法perform。根據註冊的狀態status來判斷是否確認。見⬆️。