Sidekiq 是ㄧ個功能齊全並且能夠快速處理背景任務的Ruby架構。它搭配Redis(key-value 的記憶體資料庫) ,提升性能及整合 Rails 的應用程式,例如處理大量耗時的計算、寄送電子信件、或是定期的報表製作。

接下來需要先了解以下主題

實作之前準備:

  • 一個部署在 Heroku 的 rails 專案
  • 註冊 Gandi 的域名 或是其他提供相同服務的 service provider (Mailgun)
  • 實作版本:ruby 2.6.3 以及 Rails 6.0.2

第一步:新增 Mailer

Rails 有提供內建的 Action Mailer 功能,所以只要在終端機輸入以下指令:

rails generate mailer UserMailer

之後修改預設的 [email protected] 為寄件人的 E-mail。

# app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: '[email protected]'
  layout 'mailer'
end

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
end

第二步:修改 Mailer

Mailer 也有類似像 Controller 的 action, 也會依照慣例對應到 view。在 UserMailer 中我們新增 “ welcome_email “ action, 從 params 中取得該使用者的 E-mail後,再寄出信件。

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
  default from: '[email protected]'
 
  def welcome_email
    @user = params[:user]
    @url = 'https://taskmanager0117.herokuapp.com/login'
    mail(to: @user.email, subject: "Welcome to Task Manager")
  end
end

第三步:建立 Mailer View

新增歡迎信件的樣板

# app/views/user_mailer/welcome_email.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
  </head>
  <body>
    <h1>Welcome to Task Manager, <%= @user.username %></h1>
    <p>
      You have signed up to taskmanager0117.herokuapp.com,
      your username is: <%= @user.email%><br>
    </p>
    <p>
      To login to the site, just follow this link: <%= @url %>.
    </p>
    <p>
      Thanks for joining and have a great day!
    </p>
  </body>
</html>

第四步:呼叫 Mailer

當使用者註冊成為會員時,就會收到來自系統的歡迎信件。因此在 UserController 的 create action時就要呼叫 UserMailer 執行寄信動作。

# app/controllers/users_controller.rb
def create
  @user = User.new(user_params)
  if @user.save
    UserMailer.with(user: @user).welcome_email.deliver_later
    redirect_to root_path, notice: "#{@user.username} 已註冊成功!"
  else
    @error_message = @user.errors.full_messages.to_sentence
    render :new
  end
end

第五步:修改開發環境設定檔

從 gandi 取得 SMTP 以及帳號、密碼資訊之後,再修改 development.rb 的設定檔,便可以利用 gandi 的 SMTP 伺服器寄信。 當然帳號及密碼都是需要利用環境變數存放,可以試試 figaro 來設定你自己的環境變數。 https://ithelp.ithome.com.tw/upload/images/20201010/20120868DNtjH49mKZ.png

https://ithelp.ithome.com.tw/upload/images/20201010/20120868uGjPUqjUIj.png 順利的話,以上設定完成後就可以寄信件出去了!!

第六步:安裝 sidekiq gem and Redis

首先在 Gemfile 裡新增 Sidekiq,然後記得要 bundle

gem 'sidekiq',‘~> 6.0’, ‘>= 6.0.6’ 

接著你可以在不同環境(development.rb / production.rb) 做以下設定,或是直接設定在 application.rb 都可以

config.active_job.queue_adapter = :sidekiq

如同先前所說 Sidekiq 是搭配 Redis 來做存取背景任務,所以要在終端機內輸入以下指令來安裝 Redis

brew install redis

第七步:建立 Worker

成功完成安裝 Sidekiq 後,便可以開始新增 一個 worker 來處理信件的寄送。

# app/workers/mail_worker.rb
class MailWorker
  include Sidekiq::Worker
  sidekiq_options queue: :default, retry: 3
def perform(user_id)
    user = User.find(user_id)
    if user
      UserMailer.welcome_email(user).deliver_later
    else
      errors.add(:base, '找不到使用者')
      throw(:abort)
    end
  end
end

注意:你的 perform 方法內的引數必須是簡單的屬性且能夠被 JSON 所支援,例如:字串、整數或是布林值。複雜的 Ruby 物件是不被允許的,比較好的作法就是將 user_id 傳進去方法裡,再取得所需要的 User 物件

第八步:重構程式碼

修改 Users Controller 內的 create action

# app/controllers/users_controller.rb
def create
  @user = User.new(user_params)
if @user.save
    MailWorker.perform_async(@user.id)
    redirect_to root_path, notice: "#{@user.username} 已註冊成功!"
  else
    @error_message = @user.errors.full_messages.to_sentence
    render :new
  end
end

###  注意:perform 是實體方法,而 perform_async 類別方法

在 UserMailer 的 welcome_email 方法將取得 User 物件, 並從參數中取得該使用者的 E-mail後,便可以寄出信件。

# app/mailers/user_mailer.rb
class UserMailer < ApplicationMailer
  default from: '[email protected]'
def welcome_email(user_object)
    @user = user_object
    @url = 'https://taskmanager0117.herokuapp.com/login'
mail(to: @user.email, subject: "Welcome to Task Manager")
  end
end

第九步:建立 Web UI

Sidekiq 提供Web UI 介面可以看出目前有哪些任務在執行,在routes.rb加入

# config/routes.rb
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'

https://ithelp.ithome.com.tw/upload/images/20201010/201208689t0RgBg1W7.png 這邊要注意的是 mount 並沒有提供權限控管,所以任何人都可以檢視 Web UI 介面。如果你是使用 Devise 會員系統,可以加上

authenticate :user, ->(user) { user.admin? } do
  mount Sidekiq::Web => '/sidekiq'
end

更多的詳細資料可以閱讀在 Sidekiq 的 Wiki-Monitoring

第十步:本地端測試

此步驟需要在終端機開啟

  • redis: 輸入 redis-server
  • sidekiq: 輸入 bundle exec sidekiq -q default -q mailers
  • rails: 輸入 rails s

測試註冊一個會員後,是否有收到信件?以及在 http://localhost:3000/sidekiq 是否有看到 Web UI?

參考資料: