delayed_job

来源:互联网 发布:恩牛网络 编辑:程序博客网 时间:2024/06/05 16:22

Delayed Job

AUG 21ST, 2012 | COMMENTS

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 套件。

/Gemfile
1
gem '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 來模擬):

/app/controllers/newsletters_controller.rb
123456
def deliver  @newsletter = Newsletter.find(params[:id])  sleep 10 # simulate long newsletter delivery  @newsletter.update_attribute(:delivered_at, Time.zone.now)  redirect_to newsletters_url, notice: "Delivered newsletter."end

首先將這段寄信的程式碼包到 model 裡頭,改為呼叫 Newsletter 的 deliver 方法 :

/app/controllers/newsletters_controller.rb
12345
def deliver  @newsletter = Newsletter.find(params[:id])  @newsletter.deliver  redirect_to newsletters_url, notice: "Delivered newsletter."end
/app/models/newsletter.rb
123456
class Newsletter < ActiveRecord::Base  def deliver    sleep 10 # simulate long newsletter delivery    update_attribute(:delivered_at, Time.zone.now)  endend

用 deliver 方法包起來之後,就可以呼叫 delay 方法將寄信丟到背景作業了。

/app/controllers/newsletters_controller.rb
12345
def deliver  @newsletter = Newsletter.find(params[:id])  @newsletter.delay.deliver  redirect_to newsletters_url, notice: "Delivering newsletter."end

這段程式碼會增加一筆 record 到 delayed_jobs 資料表當中,並告訴 Delayed Job 要用 deliver 方法來處理這個 instance 。

簡化

上述那段程式碼可以更加簡化,不使用整個 newsletter instance,而是透過傳遞 id 來使用 Newsletter 類別:

/app/controllers/newsletters_controller.rb
1234
def deliver  Newsletter.delay.deliver(params[:id])  redirect_to newsletters_url, notice: "Delivering newsletter."end
/app/models/newsletter.rb
12345678910
class Newsletter < ActiveRecord::Base  def self.deliver(id)    find(id).deliver  end  def deliver    sleep 10 # simulate long newsletter delivery    update_attribute(:delivered_at, Time.zone.now)  endend

Delay 方法的其他選項

Delayed Job 針對 Delay 方法提供了許多選項,其中一項是 queue,透過給定名稱的 queue,就可以指派不同的 worker 去處理對應的 queue。

/app/controllers/newsletters_controller.rb
1
Newsletter.delay(queue: "newsletter").deliver(params[:id])

另一個有用的選項是 priority,預設值是 0 ,表示優先處理。數字越小則優先處理程度越高,可設為負數。 此外,run_at 則是可以設定某個時間點再處理。

/app/controllers/newsletters_controller.rb
1
Newsletter.delay(queue: "newsletter", priority: 28, run_at: 5.minutes.from_now).deliver(params[:id])

如果某個方法每次都要用 delay 來執行,就可以考慮在該類別裡面加上 handle_asynchronously,後面以 symbol 加上要呼叫的方法:

123456
class LongTasks  def send_mailer    # Some other code  end  handle_asynchronously :send_mailer, :priority => 20end

錯誤處理

Delayed Job 也支援錯誤處理。可以設定成失敗後,會再次嘗試執行,但須注意會不會對後續其他作業產生影響。相關設定如:

/config/initializers/delayed_job_config.rb
12
Delayed::Worker.max_attempts = 5Delayed::Worker.delay_jobs = !Rails.env.test?

此段是針對 Delayed::Worker 來做設定。如果出現錯誤,就再嘗試最多 5 次;如果目前環境是 test 就停止執行。

在 Production 環境執行 Delayed Job

到目前為止,是透過 rake jobs:work 來執行 Delayed Job,但在 production 環境下,要改成執行 script 資料夾裡頭的 delayed_job script:

$ script/delayed_job start

如果在 development 環境下執行,可能會跳出錯誤訊息,說要使用 daemons gem:

/Gemfile
1
gem 'daemons'
$ bundle install

再次輸入就會顯示執行成功:

$ script/delayed_job startdelayed_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

原创粉丝点击