# Active Job 基礎
本文提供開始創建任務、將任務加入隊列和后臺執行任務的所有知識。
讀完本文,你將學到:
* 如何新建任務
* 如何將任務加入隊列
* 如何在后臺運行任務
* 如何在應用中異步發送郵件
### Chapters
1. [簡介](#%E7%AE%80%E4%BB%8B)
2. [Active Job 的目標](#active-job-%E7%9A%84%E7%9B%AE%E6%A0%87)
3. [創建一個任務](#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E4%BB%BB%E5%8A%A1)
* [創建任務](#%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1)
* [任務加入隊列](#%E4%BB%BB%E5%8A%A1%E5%8A%A0%E5%85%A5%E9%98%9F%E5%88%97)
4. [任務執行](#%E4%BB%BB%E5%8A%A1%E6%89%A7%E8%A1%8C)
* [后臺](#%E5%90%8E%E5%8F%B0)
* [設置后臺](#%E8%AE%BE%E7%BD%AE%E5%90%8E%E5%8F%B0)
5. [隊列](#%E9%98%9F%E5%88%97)
6. [回調](#%E5%9B%9E%E8%B0%83)
* [可用的回調](#%E5%8F%AF%E7%94%A8%E7%9A%84%E5%9B%9E%E8%B0%83)
* [用法](#%E7%94%A8%E6%B3%95)
7. [Action Mailer](#action-mailer)
8. [GlobalID](#globalid)
9. [異常](#%E5%BC%82%E5%B8%B8)
### 1 簡介
Active Job 是用來聲明任務,并把任務放到多種多樣的隊列后臺中執行的框架。從定期地安排清理,費用賬單到發送郵件,任何事情都可以是任務。任何可以切分為小的單元和并行執行的任務都可以用 Active Job 來執行。
### 2 Active Job 的目標
主要是確保所有的 Rails 程序有一致任務框架,即便是以 “立即執行”的形式存在。然后可以基于 Active Job 來新建框架功能和其他的 RubyGems, 而不用擔心多種任務后臺,比如 Dalayed Job 和 Resque 之間 API 的差異。之后,選擇隊列后臺更多會變成運維方面的考慮,這樣就能切換后臺而無需重寫任務代碼。
### 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`。確保任務繼承自 `ActiveJob::Base` 即可。
以下是一個任務示例:
```
class GuestsCleanupJob < ActiveJob::Base
queue_as :default
def perform(*args)
# Do something later
end
end
```
#### 3.2 任務加入隊列
將任務加入到隊列中:
```
# 將加入到隊列系統中任務立即執行
MyJob.perform_later record
```
```
# 在明天中午執行加入隊列的任務
MyJob.set(wait_until: Date.tomorrow.noon).perform_later(record)
```
```
# 一星期后執行加入到隊列的任務
MyJob.set(wait: 1.week).perform_later(record)
```
就這么簡單!
### 4 任務執行
如果沒有設置連接器,任務會立即執行。
#### 4.1 后臺
Active Job 內建支持多種隊列后臺連接器(Sidekiq、Resque、Delayed Job 等)。最新的連接器的列表詳見 [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html) 的 API 文件。
#### 4.2 設置后臺
設置隊列后臺很簡單:
```
# config/application.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
```
### 5 隊列
大多數連接器支持多種隊列。用 Active Job 可以安排任務運行在特定的隊列:
```
class GuestsCleanupJob < ActiveJob::Base
queue_as :low_priority
#....
end
```
在 `application.rb` 中通過 `config.active_job.queue_name_prefix` 來設置所有任務的隊列名稱的前綴。
```
# config/application.rb
module YourApp
class Application < Rails::Application
config.active_job.queue_name_prefix = Rails.env
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 staging_low_priority on your staging
# environment
```
默認隊列名稱的前綴是 `_`。可以設置 `config/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 staging.low_priority on your staging
# 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 在一個任務的生命周期里提供了鉤子。回調允許在任務的生命周期中觸發邏輯。
#### 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 Action Mailer
現代網站應用中最常見的任務之一是,在請求響應周期外發送 Email,這樣所有用戶不需要焦急地等待郵件的發送。Active Job 集成到 Action Mailer 里了,所以能夠簡單的實現異步發送郵件:
```
# 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 對象到任務中,來取代通常需要序列化的 class/id 對。之前任務看起來是像這樣:
```
class TrashableCleanupJob < ActiveJob::Base
def perform(trashable_class, trashable_id, depth)
trashable = trashable_class.constantize.find(trashable_id)
trashable.cleanup(depth)
end
end
```
現在可以簡化為:
```
class TrashableCleanupJob < ActiveJob::Base
def perform(trashable, depth)
trashable.cleanup(depth)
end
end
```
### 9 異常
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
```
### 反饋
歡迎幫忙改善指南質量。
如發現任何錯誤,歡迎修正。開始貢獻前,可先行閱讀[貢獻指南:文檔](http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation)。
翻譯如有錯誤,深感抱歉,歡迎 [Fork](https://github.com/ruby-china/guides/fork) 修正,或至此處[回報](https://github.com/ruby-china/guides/issues/new)。
文章可能有未完成或過時的內容。請先檢查 [Edge Guides](http://edgeguides.rubyonrails.org) 來確定問題在 master 是否已經修掉了。再上 master 補上缺少的文件。內容參考 [Ruby on Rails 指南準則](ruby_on_rails_guides_guidelines.html)來了解行文風格。
最后,任何關于 Ruby on Rails 文檔的討論,歡迎到 [rubyonrails-docs 郵件群組](http://groups.google.com/group/rubyonrails-docs)。
- Ruby on Rails 指南 (651bba1)
- 入門
- Rails 入門
- 模型
- Active Record 基礎
- Active Record 數據庫遷移
- Active Record 數據驗證
- Active Record 回調
- Active Record 關聯
- Active Record 查詢
- 視圖
- Action View 基礎
- Rails 布局和視圖渲染
- 表單幫助方法
- 控制器
- Action Controller 簡介
- Rails 路由全解
- 深入
- Active Support 核心擴展
- Rails 國際化 API
- Action Mailer 基礎
- Active Job 基礎
- Rails 程序測試指南
- Rails 安全指南
- 調試 Rails 程序
- 設置 Rails 程序
- Rails 命令行
- Rails 緩存簡介
- Asset Pipeline
- 在 Rails 中使用 JavaScript
- 引擎入門
- Rails 應用的初始化過程
- Autoloading and Reloading Constants
- 擴展 Rails
- Rails 插件入門
- Rails on Rack
- 個性化Rails生成器與模板
- Rails應用模版
- 貢獻 Ruby on Rails
- Contributing to Ruby on Rails
- API Documentation Guidelines
- Ruby on Rails Guides Guidelines
- Ruby on Rails 維護方針
- 發布記
- A Guide for Upgrading Ruby on Rails
- Ruby on Rails 4.2 發布記
- Ruby on Rails 4.1 發布記
- Ruby on Rails 4.0 Release Notes
- Ruby on Rails 3.2 Release Notes
- Ruby on Rails 3.1 Release Notes
- Ruby on Rails 3.0 Release Notes
- Ruby on Rails 2.3 Release Notes
- Ruby on Rails 2.2 Release Notes