# 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