# 6.3 异步任务及邮件发送
## 概要:
本课时讲解如何使用 sidekiq 实现异步任务,以及如何使用 ActionMailer 发送邮件。
## 知识点:
1. ActiveJob
1. sidekiq
1. ActionMailer
## 正文
### 6.3.1 ActiveJob
在 Rails 4.2 之前,经常使用 [Delayed Job](https://github.com/collectiveidea/delayed_job),[Resque](https://github.com/resque/resque),[Sidekiq](https://github.com/mperham/sidekiq) 这三种异步服务,处理后端任务,比如邮件发送,报表计算,用户动态等等。
这些任务具备一些特点:
- 执行时间长,比如为所有关注我的用户创建好友动态。
- 可以和前段操作分开执行,比如用户注册后,直接进入界面,而后端任务在稍后把欢迎邮件发出。
- 调用其他应用的 api
异步任务可以解决这些问题,但是三种常用的异步任务有各自的方法调用,Rails 4 中使用 [ActiveJob](http://guides.rubyonrails.org/active_job_basics.html) 来编写统一的操作代码,这样即便后端服务更换,也不用更改业务逻辑代码了。
### 6.3.2 Sidekiq
Sidekiq 使用 redis 储存任务,并且一个进程可以等于20个 Resque 或 DelayedJob 进程(官网上的说法)。
[redis](https://github.com/antirez/redis) 的安装非常简单,下载[安装包](http://redis.io/download),进入 src 目录:
~~~
redis-server
~~~
这样一个命令就可以启动 redis 服务了。在生产环境下,可以针对文件位置等配置,可以增加一个 redis.conf 文件,启动时选择:
~~~
redis-server .conf/redis.conf
~~~
这里我做了两个修改:
~~~
dir ./db/redis/ [1]
logfile ./log/redis.log [2]
# requirepass foobar
~~~
[1] 在我们的 db 下建立 redis 目录,放置 redis 数据库文件[2] redis 日志放入项目日志目录中[3] 我们在开发环境下去掉密码校验
安装 sidekiq 需要两个 gem:
~~~
gem 'sidekiq'
gem 'sinatra', :require => nil
~~~
通常我们需要 sidekiq 的管理界面,来查看当前的任务队列情况,sinatra 可以单独启动这个管理服务, 我们修改一下 routes:
`config/routes.rb`
~~~
Rails.application.routes.draw do
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
~~~
我们再增加一个 sidekiq 的配置文件 `config/sidekiq.yml`,然后运行它:
~~~
sidekiq -C config/sidekiq.yml
s
ss
sss sss ss
s sss s ssss sss ____ _ _ _ _
s sssss ssss / ___|(_) __| | ___| | _(_) __ _
s sss \___ \| |/ _` |/ _ \ |/ / |/ _` |
s sssss s ___) | | (_| | __/ <| | (_| |
ss s s |____/|_|\__,_|\___|_|\_\_|\__, |
s s s |_|
s s
sss
sss
~~~
这样便启动了 sidekiq 服务,我们用它来完成异步任务。在用 Rails 使用 sidekiq 前,需要在 `config/application.rb` 声明一下:
~~~
config.active_job.queue_adapter = :sidekiq
~~~
### 6.3.3 异步任务,Job
我们用 generate 来创建一个任务类:
~~~
rails generate job order_create
~~~
OrderCreateJob 用来处理订单创建时,需要额外完成的一些操作,比如,向这个订单的用户发送一封 “订单已确认” 的邮件。我们使用 after_create 这个回调,来触发这个异步任务。
~~~
class Order < ActiveRecord::Base
....
after_create :send_create_email
def send_create_email
OrderCreateJob.perform_later(self)
end
~~~
~~~
class OrderCreateJob < ActiveJob::Base
queue_as :default
def perform(order)
...
end
end
~~~
### 6.3.4 使用 ActionMailer 发送邮件
[ActionMailer](https://github.com/rails/rails/tree/master/actionmailer) 是一个邮件发送 gem,它使用了 ActionController 类和 [Mail](https://github.com/mikel/mail) 发送邮件,邮件可以使用视图文件,也可以是 txt 邮件。它也可以接收邮件,具体可参考[手册](http://guides.rubyonrails.org/action_mailer_basics.html#receiving-emails) 或 [文档](https://github.com/mikel/mail#getting-emails-from-a-pop-server)。
我们来创建一个处理订单邮件发送的控制器:
~~~
rails generate mailer OrderMailer
create app/mailers/order_mailer.rb
create app/mailers/application_mailer.rb
invoke erb
create app/views/order_mailer
create app/views/layouts/mailer.text.erb
create app/views/layouts/mailer.html.erb
invoke rspec
create spec/mailers/order_mailer_spec.rb
create spec/mailers/previews/order_mailer_preview.rb
~~~
我们为 app/mailers/order_mailer.rb 增加一个发送方法:
~~~
class OrderMailer < ApplicationMailer
def confirm_email(order)
@user = order.user
@order = order
mail(to: @user.email, subject: "您的订单 #{@order.number} 已经确认")
end
end
~~~
ActionMailer 为我们创建了邮件模板和 html、text 两种格式的邮件,我们分别制作相同的内容,具体请参照 [第六章代码](https://github.com/liwei78/rails-practice-code/tree/master/chapter_6/shop)。如果同时存在 html 和 text 视图,ActionMailer 会采用 Multipart 形式将他们发送出去。
进入终端来测试邮件:
~~~
order = Order.last
OrderMailer.confirm_email(o).deliver_later
Enqueued ActionMailer::DeliveryJob (Job ID: ...) to Sidekiq(mailers) with arguments: ...
~~~
`deliver_later` 是将邮件发送任务队列,`deliver_now` 是将邮件立刻发送。区别在于,`deliver_later` 不会阻塞当前进程,比如我们页面中会立刻进入下一个页面,而 `deliver_now` 会等待邮件发送完成,才会进行下一步。
更 ActionMailer 的介绍请查看 [Action Mailer Basics](http://guides.rubyonrails.org/action_mailer_basics.html)。
回到 `OrderCreateJob`, 我们把邮件发送加入到 `perform` 方法中
~~~
class OrderCreateJob < ActiveJob::Base
queue_as :default
def perform(order)
OrderMailer.confirm_email(order).deliver_now
end
end
~~~
因为我们已经使用异步任务,所以直接使用 deliver_now 发送邮件了。
更多 ActionMailer 的配置,在 [这里](http://guides.rubyonrails.org/configuring.html#configuring-action-mailer) 有详细的介绍。
sidekiq 可以完成其他异步的业务逻辑,比如确认订单后的积分计算,向关注我的好友发送动态等。因为我们在 routes 中增加了 sidekiq 的管理界面地址,所以访问 [http://localhost:3000/sidekiq](http://localhost:3000/sidekiq) 可以查看当前任务执行情况。
- 写在前面
- 第一章 Ruby on Rails 概述
- Ruby on Rails 开发环境介绍
- Rails 文件简介
- 用户界面(UI)设计
- 第二章 Rails 中的资源
- 应用 scaffold 命令创建资源
- REST 架构
- 深入路由(routes)
- 第三章 Rails 中的视图
- 布局和辅助方法
- 表单
- 视图中的 AJAX 交互
- 模板引擎的使用
- 第四章 Rails 中的模型
- 模型的基础操作
- 深入模型查询
- 模型中的关联关系
- 模型中的校验
- 模型中的回调
- 第五章 Rails 中的控制器
- 控制器中的方法
- 控制器中的逻辑
- 第六章 Rails 的配置及部署
- Assets 管理
- 缓存及缓存服务
- 异步任务及邮件发送
- I18n
- 生产环境部署
- 常用 Gem
- 写在后面