1 什麼是主動工作?
Active Job 是一個框架,用於宣告作業並使它們在各種 排隊後端。這些工作可以是定期安排的一切 清理,計費,郵寄。任何可以切碎的東西 分成小的工作單元並並行執行,真的。
2 主動工作的目的
重點是確保所有 Rails 應用程式都具有作業基礎架構 到位。然後我們可以在其基礎上構建框架功能和其他 gem, 無需擔心各種作業執行程式之間的 API 差異,例如 延遲的工作和 Resque。選擇您的排隊後端變得更具有操作性 那麼關心。而且您將能夠在它們之間切換而無需重寫 你的工作。
預設情況下,Rails 帶有非同步排隊實現 使用程序內執行緒池執行作業。作業將非同步執行,但任何 佇列中的作業將在重新啟動時刪除。
3 建立工作
本節將提供建立作業和排隊的分步指南。
3.1 建立作業
Active Job 提供了一個 Rails 生成器來建立作業。下面將建立一個
app/jobs
中的工作(在 test/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
如果你不想使用生成器,你可以在裡面建立你自己的檔案
app/jobs
,只要確保它繼承自 ApplicationJob
。
這是一份工作的樣子:
class GuestsCleanupJob < ApplicationJob
queue_as :default
def perform(*guests)
# Do something later
end
end
請注意,您可以根據需要使用任意數量的引數定義 perform
。
3.2 將作業入隊
使用 perform_later
和可選的 set
對作業進行排隊。像這樣:
# 排隊系統一啟動就將要執行的作業入隊
# 自由。
GuestsCleanupJob.perform_later guest
# 將明天中午要執行的作業排入佇列。
GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
# 將要在 1 周後執行的作業加入佇列。
GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
# `perform_now` 和 `perform_later` 會在引擎蓋下呼叫 `perform` 所以
# 您可以傳遞與後者中定義的一樣多的引數。
GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')
而已!
4 工作執行
為了在生產中排隊和執行作業,您需要設定一個排隊後端, 也就是說,你需要決定 Rails 應該使用的第 3 方佇列庫。 Rails 本身只提供了一個程序內排隊系統,它只將作業儲存在 RAM 中。 如果程序崩潰或機器被重置,那麼所有未完成的作業都會丟失 預設非同步後端。這對於較小的應用程式或非關鍵工作可能沒問題,但大多數 生產應用程式需要選擇一個持久的後端。
4.1 後端
Active Job 具有用於多個佇列後端的內建介面卡(Sidekiq、
Resque、延遲作業等)。獲取最新的介面卡列表
請參閱 ActiveJob::QueueAdapters
的 API 文件。
4.2 設定後端
您可以輕鬆設定排隊後端:
# 配置/應用程式.rb
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
您還可以基於每個作業配置您的後端:
class GuestsCleanupJob < ApplicationJob
self.queue_adapter = :resque
# ...
end
# 現在你的工作將使用 `resque` 作為它的後端佇列介面卡,覆蓋什麼
# 在 `config.active_job.queue_adapter` 中配置。
4.3 啟動後端
由於作業與 Rails 應用程式並行執行,因此大多數排隊庫 要求您啟動特定於圖書館的排隊服務(除了 啟動您的 Rails 應用程式)以使作業處理工作。參考圖書館 有關啟動佇列後端的說明的文件。
這是一個不完整的文件列表:
5 佇列
大多數介面卡支援多個佇列。使用 Active Job,您可以安排
使用 queue_as
在特定佇列上執行的作業:
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
您可以使用字首為所有作業的佇列名稱
application.rb
中的 config.active_job.queue_name_prefix
:
# 配置/應用程式.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
end
end
# app/jobs/guests_cleanup_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
# 現在你的工作將在佇列 production_low_priority 上執行
# 生產環境和 staging_low_priority
# 在你的臨時環境中
您還可以在每個作業的基礎上配置字首。
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
self.queue_name_prefix = nil
# ...
end
# 現在你的工作佇列不會被字首,覆蓋什麼
# 在 `config.active_job.queue_name_prefix` 中配置。
預設佇列名稱字首定界符是“_”。這可以透過設定來改變
application.rb
中的 config.active_job.queue_name_delimiter
:
# 配置/應用程式.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_job.rb
class GuestsCleanupJob < ApplicationJob
queue_as :low_priority
# ...
end
# 現在你的工作將在佇列 production.low_priority 上執行
# 生產環境和 staging.low_priority
# 在你的臨時環境中
如果您想更多地控制作業將執行的佇列,您可以傳遞 :queue
set
的選項:
MyJob.set(queue: :another_queue).perform_later(record)
要從作業級別控制佇列,您可以將一個塊傳遞給 queue_as
。這
塊將在作業上下文中執行(因此它可以訪問 self.arguments
),
它必須返回佇列名稱:
class ProcessVideoJob < ApplicationJob
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 Callbacks
Active Job 提供掛載機制以在作業的生命週期內觸發邏輯。喜歡 Rails 中的其他 callbacks,可以將 callbacks 實現為普通方法 並使用宏樣式的類方法將它們註冊為 callbacks:
class GuestsCleanupJob < ApplicationJob
queue_as :default
around_perform :around_cleanup
def perform
# Do something later
end
private
def around_cleanup
# Do something before perform
yield
# Do something after perform
end
end
宏風格的類方法也可以接收一個塊。考慮使用這個 如果程式碼塊內的程式碼太短以至於它適合一行。 例如,您可以為每個入隊的作業傳送指標:
class ApplicationJob < ActiveJob::Base
before_enqueue { |job| $statsd.increment "#{job.class.name.underscore}.enqueue" }
end
6.1 可用 callbacks
7 Action Mailer
現代 Web 應用程式中最常見的工作之一是向外部發送電子郵件 請求-回應週期,所以使用者不必等待它。在職工作 與 Action Mailer 整合,因此您可以輕鬆地非同步傳送電子郵件:
# 如果您想立即傳送電子郵件,請使用 #deliver_now
UserMailer.welcome(@user).deliver_now
# 如果您想透過 Active Job 傳送電子郵件,請使用 #deliver_later
UserMailer.welcome(@user).deliver_later
使用來自 Rake 任務的非同步佇列(例如,
使用 .deliver_later
傳送電子郵件)通常不起作用,因為 Rake 會
可能結束,導致程序內執行緒池在任何/全部之前被刪除
的 .deliver_later
電子郵件被處理。為避免此問題,請使用
.deliver_now
或在開發中執行持久佇列。
8 國際化
每個作業都使用建立作業時設定的 I18n.locale
。如果您傳送,這很有用
非同步傳送電子郵件:
I18n.locale = :eo
UserMailer.welcome(@user).deliver_later # Email will be localized to Esperanto.
9 支援的引數型別
預設情況下,ActiveJob 支援以下型別的引數:
- 基本型別(
NilClass
、String
、Integer
、Float
、BigDecimal
、TrueClass
、FalseClass
) Symbol
Date
Time
DateTime
ActiveSupport::TimeWithZone
ActiveSupport::Duration
-
Hash
(Key 應該是String
或Symbol
型別) ActiveSupport::HashWithIndifferentAccess
Array
Range
Module
Class
9.1 全域性 ID
Active Job 支援 GlobalID 作為引數。這使得透過直播成為可能 Active Record 物件到您的工作,而不是您擁有的類/ID 對 手動反序列化。以前,作業看起來像這樣:
class TrashableCleanupJob < ApplicationJob
def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
end
end
現在你可以簡單地做:
class TrashableCleanupJob < ApplicationJob
def perform(trashable, depth)
trashable.cleanup(depth)
end
end
這適用於任何在 GlobalID::Identification
中混合的類,其中
預設情況下已混入 Active Record 類。
9.2 序列化程式
您可以擴充套件支援的引數型別列表。您只需要定義自己的序列化程式:
# app/serializers/money_serializer.rb
class MoneySerializer < ActiveJob::Serializers::ObjectSerializer
# Checks if an argument should be serialized by this serializer.
def serialize?(argument)
argument.is_a? Money
end
# Converts an object to a simpler representative using supported object types.
# The recommended representative is a Hash with a specific key. Keys can be of basic types only.
# You should call `super` to add the custom serializer type to the hash.
def serialize(money)
super(
"amount" => money.amount,
"currency" => money.currency
)
end
# Converts serialized value into a proper object.
def deserialize(hash)
Money.new(hash["amount"], hash["currency"])
end
end
並將此序列化程式新增到列表中:
# config/initializers/custom_serializers.rb
Rails.application.config.active_job.custom_serializers << MoneySerializer
請注意,不支援在初始化期間自動載入可過載程式碼。因此推薦
設定僅載入一次的序列化程式,例如透過像這樣修改 config/application.rb
:
# 配置/應用程式.rb
module YourApp
class Application < Rails::Application
config.autoload_once_paths << Rails.root.join('app', 'serializers')
end
end
10 例外
可以使用以下方法處理作業執行期間引發的異常
rescue_from
:
class GuestsCleanupJob < ApplicationJob
queue_as :default
rescue_from(ActiveRecord::RecordNotFound) do |exception|
# Do something with the exception
end
def perform
# Do something later
end
end
如果作業的異常未獲救,則該作業被稱為“失敗”。
10.1 重試或丟棄失敗的作業
除非另有配置,否則不會重試失敗的作業。
可以使用 retry_on
或
discard_on
,分別。例如:
class RemoteServiceJob < ApplicationJob
retry_on CustomAppException # defaults to 3s wait, 5 attempts
discard_on ActiveJob::DeserializationError
def perform(*args)
# Might raise CustomAppException or ActiveJob::DeserializationError
end
end
10.2 反序列化
GlobalID 允許序列化傳遞給 #perform
的完整 Active Record 物件。
如果在作業入隊之後但在 #perform
之前刪除透過的記錄
方法被稱為 Active Job 將引發 ActiveJob::DeserializationError
例外。
11 工作測試
您可以在 測試指南。
回饋
我們鼓勵您幫助提高本指南的品質。
如果您發現任何拼寫錯誤或資訊錯誤,請提供回饋。 要開始回饋,您可以閱讀我們的 回饋 部分。
您還可能會發現不完整的內容或不是最新的內容。 請務必為 main 新增任何遺漏的文件。假設是 非穩定版指南(edge guides) 請先驗證問題是否已經在主分支上解決。 請前往 Ruby on Rails 指南寫作準則 查看寫作風格和慣例。
如果由於某種原因您發現要修復的內容但無法自行修補,請您 提出 issue。
關於 Ruby on Rails 的任何類型的討論歡迎提供任何文件至 rubyonrails-docs 討論區。