更多內容 rubyonrails.org: 更多內容

Active Job 基礎

本篇提供所有建立任務、將任務加入排程,執行背景任務的所有知識。

讀完本篇,您將了解:

1 簡介

Active Job 是用來宣告任務,並把任務放到多種佇列後台執行的框架。這些任務可以是平常的系統定時清理、收費方式改變通知、或是定時寄送郵件等任務。任何可以細分的工作與同步執行的事情都可以用 Active Job 來做。

2 Active Job 存在目的

主要確保 Rails 應用程式有個一致的背景任務框架,不做背景任務,要即時執行也可以。接著便可以有基於 Active Job 打造的功能、Gem 誕生,而無需擔心各種佇列後台,像是 Delayed Job 和 Resque 之間的 API 差異。選擇佇列後台變成一種運維方面的考量,而切換後台也無需修改任務本身的實作。

3 新建任務

本節提供建立任務,將任務加入排程的詳細教學。

3.1 建立任務

Active Job 提供了 Rails 產生器來建立任務。以下會在 app/jobs 建立一件新任務:

$ bin/rails generate job guests_cleanup
invoke  test_unit
create    test/jobs/guests_cleanup_job_test.rb
create  app/jobs/guests_cleanup_job.rb

也可以建立跑在特定佇列上的任務:

$ bin/rails generate job guests_cleanup --queue urgent

可以看出來,建立任務就和使用其他的 Rails 產生器一樣簡單。

若不想使用產生器,也可以自己在 app/jobs 下建立檔案,只要確保任務是繼承自 ActiveJob::Base 的類別即可。

以下是任務的程式範例:

class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

3.2 任務排程

將任務加入排程:

# Enqueue a job to be performed as soon the queueing system is free.
MyJob.perform_later record

# Enqueue a job to be performed tomorrow at noon.
MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record)

# Enqueue a job to be performed 1 week from now.
MyJob.set(wait: 1.week).perform_later(record)

就這麼簡單!

4 執行任務

若無設定連接器,任務會即刻執行。

4.1 後台

Active Job 內建支援多種佇列後台的連接器(Sidekiq、Resque、Delayed Job 等)。完整連接器列表請見 ActiveJob::QueueAdapters 的 API 文件。

4.2 切換後台

切換後台的連接器非常簡單:

# be sure to have the adapter gem in your Gemfile and follow the adapter specific
# installation and deployment instructions
Rails.Application.config.active_job.queue_adapter = :sidekiq

5 佇列

多數的連接器都支持多種佇列。用 Active Job 可以將任務放到特定的佇列裡執行:

class GuestsCleanupJob < ActiveJob::Base
  queue_as :low_priority
  #....
end

預設佇列名稱的前綴為 \_。可以在 application.rb 修改config.active_job.queue_name_delimiter` 來修改:

# config/application.rb
module YourApp
  class Application < Rails::Application
    config.active_job.queue_name_prefix = Rails.env
    config.active_job.queue_name_delimiter = '.'
  end
end

# app/jobs/guests_cleanup.rb
class GuestsCleanupJob < ActiveJob::Base
  queue_as :low_priority
  #....
end

# Now your job will run on queue production_low_priority on your
# production environment and on beta_low_priority on your beta
# environment

若需要更細緻的控制任務的執行,可以傳 :queue#set 方法。

MyJob.set(queue: :another_queue).perform_later(record)

要在任務層級控制佇列,可以傳一個區塊給 queue_as。區塊會在任務的上下文裡執行(也就是拿的到 `self.arguments),記得要回傳佇列的名稱:

class ProcessVideoJob < ActiveJob::Base
  queue_as do
    video = self.arguments.first
    if video.owner.premium?
      :premium_videojobs
    else
      :videojobs
    end
  end

  def perform(video)
    # do process video
  end
end

ProcessVideoJob.perform_later(Video.last)

確保後台程式知道佇列的名稱是什麼。某些後台可能需要明確指定佇列。

6 回呼

Active Job 在任務生命週期裡的每個階段都有提供 hooks。回呼允許在任務生命週期裡觸發事件來執行程式邏輯。

6.1 可用的回呼

  • before_enqueue
  • around_enqueue
  • after_enqueue
  • before_perform
  • around_perform
  • after_perform

6.2 用法

class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  before_enqueue do |job|
    # do something with the job instance
  end

  around_perform do |job, block|
    # do something before perform
    block.call
    # do something after perform
  end

  def perform
    # Do something later
  end
end

7 ActionMailer

現代網路應用最常見的任務之一是在請求響應週期之外發送 Email,減去使用者等待的時間。Active Job 已與 Action Mailer 整合,異步寄送信件只需使用 deliver_later 即可:

# If you want to send the email now use #deliver_now
UserMailer.welcome(@user).deliver_now

# If you want to send the email through Active Job use #deliver_later
UserMailer.welcome(@user).deliver_later

8 GlobalID

Active Job 支持使用 GlobalID 作為參數。這使得任務可以傳入 Active Record 物件,而不只是需要額外處理的類別名稱或 ID。使用類別和 ID 的任務看起來會像是:

class TrashableCleanupJob
  def perform(trashable_class, trashable_id, depth)
    trashable = trashable_class.constantize.find(trashable_id)
    trashable.cleanup(depth)
  end
end

可以傳入 Active Record 物件來簡化:

class TrashableCleanupJob
  def perform(trashable, depth)
    trashable.cleanup(depth)
  end
end

以上對任何混入 GlobalID::Identification 的類都有效,Active Model 的類別預設皆有混入 GlobalID::Identification

9 Exceptions

Active Job 提供捕捉任務執行期間發生異常的方法:

class GuestsCleanupJob < ActiveJob::Base
  queue_as :default

  rescue_from(ActiveRecord::RecordNotFound) do |exception|
   # do something with the exception
  end

  def perform
    # Do something later
  end
end

反饋

歡迎幫忙改善指南的品質。

如發現任何錯誤之處,歡迎修正。開始貢獻前,可以先閱讀貢獻指南:文件

翻譯如有錯誤,深感抱歉,歡迎 Fork 修正,或至此處回報

文章可能有未完成或過時的內容。請先檢查 Edge Guides 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 Ruby on Rails 指南準則來了解行文風格。

最後,任何關於 Ruby on Rails 文件的討論,歡迎至 rubyonrails-docs 郵件論壇