本課時(shí)講解如何使用 sidekiq 實(shí)現(xiàn)異步任務(wù),以及如何使用 ActionMailer 發(fā)送郵件。
在 Rails 4.2 之前,經(jīng)常使用 Delayed Job,Resque,Sidekiq 這三種異步服務(wù),處理后端任務(wù),比如郵件發(fā)送,報(bào)表計(jì)算,用戶動(dòng)態(tài)等等。
這些任務(wù)具備一些特點(diǎn):
異步任務(wù)可以解決這些問題,但是三種常用的異步任務(wù)有各自的方法調(diào)用,Rails 4 中使用 ActiveJob 來編寫統(tǒng)一的操作代碼,這樣即便后端服務(wù)更換,也不用更改業(yè)務(wù)邏輯代碼了。
Sidekiq 使用 redis 儲(chǔ)存任務(wù),并且一個(gè)進(jìn)程可以等于20個(gè) Resque 或 DelayedJob 進(jìn)程(官網(wǎng)上的說法)。
redis 的安裝非常簡單,下載安裝包,進(jìn)入 src 目錄:
redis-server
這樣一個(gè)命令就可以啟動(dòng) redis 服務(wù)了。在生產(chǎn)環(huán)境下,可以針對(duì)文件位置等配置,可以增加一個(gè) redis.conf 文件,啟動(dòng)時(shí)選擇:
redis-server .conf/redis.conf
這里我做了兩個(gè)修改:
dir ./db/redis/ [1]
logfile ./log/redis.log [2]
# requirepass foobar
[1] 在我們的 db 下建立 redis 目錄,放置 redis 數(shù)據(jù)庫文件 [2] redis 日志放入項(xiàng)目日志目錄中 [3] 我們?cè)陂_發(fā)環(huán)境下去掉密碼校驗(yàn)
安裝 sidekiq 需要兩個(gè) gem:
gem 'sidekiq'
gem 'sinatra', :require => nil
通常我們需要 sidekiq 的管理界面,來查看當(dāng)前的任務(wù)隊(duì)列情況,sinatra 可以單獨(dú)啟動(dòng)這個(gè)管理服務(wù), 我們修改一下 routes:
config/routes.rb
Rails.application.routes.draw do
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
我們?cè)僭黾右粋€(gè) sidekiq 的配置文件 config/sidekiq.yml
,然后運(yùn)行它:
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
這樣便啟動(dòng)了 sidekiq 服務(wù),我們用它來完成異步任務(wù)。在用 Rails 使用 sidekiq 前,需要在 config/application.rb
聲明一下:
config.active_job.queue_adapter = :sidekiq
我們用 generate 來創(chuàng)建一個(gè)任務(wù)類:
rails generate job order_create
OrderCreateJob 用來處理訂單創(chuàng)建時(shí),需要額外完成的一些操作,比如,向這個(gè)訂單的用戶發(fā)送一封 “訂單已確認(rèn)” 的郵件。我們使用 after_create 這個(gè)回調(diào),來觸發(fā)這個(gè)異步任務(wù)。
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
ActionMailer 是一個(gè)郵件發(fā)送 gem,它使用了 ActionController 類和 Mail 發(fā)送郵件,郵件可以使用視圖文件,也可以是 txt 郵件。它也可以接收郵件,具體可參考手冊(cè) 或 文檔。
我們來創(chuàng)建一個(gè)處理訂單郵件發(fā)送的控制器:
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
我們?yōu)?app/mailers/order_mailer.rb 增加一個(gè)發(fā)送方法:
class OrderMailer < ApplicationMailer
def confirm_email(order)
@user = order.user
@order = order
mail(to: @user.email, subject: "您的訂單 #{@order.number} 已經(jīng)確認(rèn)")
end
end
ActionMailer 為我們創(chuàng)建了郵件模板和 html、text 兩種格式的郵件,我們分別制作相同的內(nèi)容,具體請(qǐng)參照 第六章代碼。如果同時(shí)存在 html 和 text 視圖,ActionMailer 會(huì)采用 Multipart 形式將他們發(fā)送出去。
進(jìn)入終端來測(cè)試郵件:
order = Order.last
OrderMailer.confirm_email(o).deliver_later
Enqueued ActionMailer::DeliveryJob (Job ID: ...) to Sidekiq(mailers) with arguments: ...
deliver_later
是將郵件發(fā)送任務(wù)隊(duì)列,deliver_now
是將郵件立刻發(fā)送。區(qū)別在于,deliver_later
不會(huì)阻塞當(dāng)前進(jìn)程,比如我們頁面中會(huì)立刻進(jìn)入下一個(gè)頁面,而 deliver_now
會(huì)等待郵件發(fā)送完成,才會(huì)進(jìn)行下一步。
更 ActionMailer 的介紹請(qǐng)查看 Action Mailer Basics。
回到 OrderCreateJob
, 我們把郵件發(fā)送加入到 perform
方法中
class OrderCreateJob < ActiveJob::Base
queue_as :default
def perform(order)
OrderMailer.confirm_email(order).deliver_now
end
end
因?yàn)槲覀円呀?jīng)使用異步任務(wù),所以直接使用 deliver_now 發(fā)送郵件了。
更多 ActionMailer 的配置,在 這里 有詳細(xì)的介紹。
sidekiq 可以完成其他異步的業(yè)務(wù)邏輯,比如確認(rèn)訂單后的積分計(jì)算,向關(guān)注我的好友發(fā)送動(dòng)態(tài)等。因?yàn)槲覀冊(cè)?routes 中增加了 sidekiq 的管理界面地址,所以訪問 http://localhost:3000/sidekiq 可以查看當(dāng)前任務(wù)執(zhí)行情況。