Delayed Job 是一套非同步排程套件。
有時候,當必須執行 process time 較久的 request 時,會因為要等待此 request 執行完畢而無法再做其他的 request 導致效率低落,此時如果把 process time 較久的 request 移到背後去執行,那就可以把原本要等候的時間拿來處理其他事。
Rails 目前已有許多可執行背景作業的套件,而當中的 Delayed Job 其特點是使用與此 application 相同的資料庫,並簡化許多的設定。此外,Delayed Job 提供了一個非常簡單的 interface,在呼叫任何方法之前加上 delay
就可以改為背景作業。以下將以寄信作為範例:
安裝
Delayed Job 有許多不同的套件,由於此範例使用的是 ActiveRecord,因此選擇 delayed_job_active_record
套件。
$ bundle install
安裝套件:
$ rails g delayed_job:active_record
create script/delayed_job
chmod script/delayed_job
create db/migrate/20120109185353_create_delayed_jobs.rb
由於會產生 migration 檔案,因此要再執行 rake db:migrate
來產生資料表。
設定完了之後,就執行 Delayed Job 的 Rake task:jobs:work
以開始背景作業。
$ rake jobs:work
[Worker(host:noonoo.home pid:3031)] Starting job worker
使用
假設此範例的寄信需要花費 10 秒鐘(用 sleep
來模擬):
首先將這段寄信的程式碼包到 model 裡頭,改為呼叫 Newsletter
的 deliver
方法 :
用 deliver
方法包起來之後,就可以呼叫 delay
方法將寄信丟到背景作業了。
這段程式碼會增加一筆 record 到 delayed_jobs
資料表當中,並告訴 Delayed Job 要用 deliver
方法來處理這個 instance 。
簡化
上述那段程式碼可以更加簡化,不使用整個 newsletter instance,而是透過傳遞 id
來使用 Newsletter
類別:
Delay 方法的其他選項
Delayed Job 針對 Delay
方法提供了許多選項,其中一項是 queue
,透過給定名稱的 queue,就可以指派不同的 worker 去處理對應的 queue。
另一個有用的選項是 priority
,預設值是 0 ,表示優先處理。數字越小則優先處理程度越高,可設為負數。 此外,run_at
則是可以設定某個時間點再處理。
如果某個方法每次都要用 delay 來執行,就可以考慮在該類別裡面加上 handle_asynchronously
,後面以 symbol 加上要呼叫的方法:
錯誤處理
Delayed Job 也支援錯誤處理。可以設定成失敗後,會再次嘗試執行,但須注意會不會對後續其他作業產生影響。相關設定如:
/config/initializers/delayed_job_config.rb 此段是針對 Delayed::Worker
來做設定。如果出現錯誤,就再嘗試最多 5 次;如果目前環境是 test
就停止執行。
在 Production 環境執行 Delayed Job
到目前為止,是透過 rake jobs:work
來執行 Delayed Job,但在 production 環境下,要改成執行 script 資料夾裡頭的 delayed_job
script:
$ script/delayed_job start
如果在 development 環境下執行,可能會跳出錯誤訊息,說要使用 daemons
gem:
$ bundle install
再次輸入就會顯示執行成功:
$ script/delayed_job start
delayed_job: process with pid 1672 started.
如果要停止,就輸入:
$ script/delayed_job stop
若要在 Capistrano 上使用 Delayed Job,可至 wiki 查看。
若要使用 web 方式來監控 job queue,可使用 delayed_job_web gem。
其他非同步排成套件還有 Resque、Sidekiq 等。
source: RailsCasts