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