鍍金池/ 教程/ Ruby/ 6.3 異步任務(wù)及郵件發(fā)送
寫在后面
寫在前面
第六章 Rails 的配置及部署
第四章 Rails 中的模型
4.4 模型中的校驗(yàn)(Validates)
1.3 用戶界面(UI)設(shè)計(jì)
6.5 生產(chǎn)環(huán)境部署
3.2 表單
4.3 模型中的關(guān)聯(lián)關(guān)系(Relations)
4.5 模型中的回調(diào)(Callback)
第五章 Rails 中的控制器
4.2 深入模型查詢
5.2 控制器中的方法
6.2 緩存
3.4 模板引擎的使用
6.4 I18n
第一章 Ruby on Rails 概述
6.6 常用 Gem
1.2 Rails 文件簡介
2.2 REST 架構(gòu)
2.3 深入路由(routes)
第三章 Rails 中的視圖
6.3 異步任務(wù)及郵件發(fā)送
第二章 Rails 中的資源
3.3 視圖中的 AJAX 交互

6.3 異步任務(wù)及郵件發(fā)送

概要:

本課時(shí)講解如何使用 sidekiq 實(shí)現(xiàn)異步任務(wù),以及如何使用 ActionMailer 發(fā)送郵件。

知識(shí)點(diǎn):

  1. ActiveJob
  2. sidekiq
  3. ActionMailer

正文

6.3.1 ActiveJob

在 Rails 4.2 之前,經(jīng)常使用 Delayed Job,Resque,Sidekiq 這三種異步服務(wù),處理后端任務(wù),比如郵件發(fā)送,報(bào)表計(jì)算,用戶動(dòng)態(tài)等等。

這些任務(wù)具備一些特點(diǎn):

  • 執(zhí)行時(shí)間長,比如為所有關(guān)注我的用戶創(chuàng)建好友動(dòng)態(tài)。
  • 可以和前段操作分開執(zhí)行,比如用戶注冊(cè)后,直接進(jìn)入界面,而后端任務(wù)在稍后把歡迎郵件發(fā)出。
  • 調(diào)用其他應(yīng)用的 api

異步任務(wù)可以解決這些問題,但是三種常用的異步任務(wù)有各自的方法調(diào)用,Rails 4 中使用 ActiveJob 來編寫統(tǒng)一的操作代碼,這樣即便后端服務(wù)更換,也不用更改業(yè)務(wù)邏輯代碼了。

6.3.2 Sidekiq

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

6.3.3 異步任務(wù),Job

我們用 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

6.3.4 使用 ActionMailer 發(fā)送郵件

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í)行情況。