Edited at

Rails 4.2で導入されたActive Jobを使ってみよう

More than 1 year has passed since last update.


Active Jobとは?

Ruby on Rails(以下Rails) 4.2からバックグラウンドでジョブを実行するActive Jobが利用できるようになりました。

Active Jobを利用することによって、メールの送信や、バッチ処理などをバックグラウンドで実行することが可能になります。

Railsで利用できるバックグラウンドでのジョブ実行の仕組みとしては、Delayed Job, Resque, Sidekiqなどがありますが、Active Jobはこれらのアダプタとなって動作します。

Active Jobは、ジョブスケジューリングを抽象化するAPIを備えており、異なるジョブ実行の仕組みに移行(たとえばDelayed JobからResqueへ移行)しても、コードをほとんど変えることなく実行することができます。


Active Jobを使うための準備


Resqueのセットアップ

今回はActive JobをResqueのアダプタとして使用するので、Resqueをセットアップします。

Resqueは、Redisが必要なのでインストールを済ませておきます。

MacでHomebrewを利用している場合は、以下のようにしてインストールします。

$ brew install redis

次にResqueをセットアップします。

以下のようにGemfileに指定して、bundle installを実行します。

gem 'resque'

gem 'resque-scheduler'

インストールできたら、初期設定ファイルとrakeタスクの追加を行います。

config/initializers/resque.rbを以下のような内容で作成します。

Resque.redis = Redis.new(host: 'localhost', post: 6379)

Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }

lib/tasks/resque.rakeを以下の内容で作成します。

require 'resque/tasks'

require 'resque/scheduler/tasks'

namespace :resque do
task setup: :environment do
ENV['TERM_CHILD'] ||= '1'
ENV['QUEUE'] ||= '*'
require 'resque'
require 'resque-scheduler'
end
end


Active Jobのセットアップ

以下のような初期設定ファイルを作成し、queue_adapterとしてResqueを指定します。

config/initializers/active_job.rb

ActiveJob::Base.queue_adapter = :resque


Active Jobを実際に使ってみる

例として、Active Jobを使ってバックグラウンドでメールを送信してみます。

Webサービスで、ユーザーが登録した際にWelcomeメールを送信することを考えます。(今回は、Rdailyというブログ用のアプリケーションを使っています)


メーラの作成

以下のコマンドでUserMailerを作成します。

$ rails g mailer user_mailer

送信用のメソッドとビューを追加します。

app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base

default from: "from@example.com"

def registered(user)
mail(to: user.email, subject: 'Welcome to Rdaily!')
end
end

app/views/user_mailer/registered.text.erb

Dear. <%= @user.name %>

Welcome to Rdaily!


ジョブの作成

メーラを作成したので、次はActive Jobのジョブを作成します。

Rails 4.2ではActive Jobの導入に伴い、以下のようなジェネレータが利用できるようになっています。

$ rails g job user_registered_mailer

...
create app/jobs/user_registered_mailer_job.rb

作成されたapp/jobs/user_registered_mailer_job.rbを以下のように編集します。

class UserRegisteredMailerJob < ActiveJob::Base

queue_as :email

def perform(user)
UserMailer.registered(user).deliver_now
end
end


アプリケーション内でのジョブの使用

作成したジョブを利用するには、コントローラなどで以下のようにジョブのインスタンスを作成し、待ち時間を指定して#perform_laterメソッドを呼び出します。

今回のアプリケーションの例では、ユーザーを作成後に以下のように呼び出しています。

class Account::UsersController < ApplicationController

...

def create
@user = User.new(user_params)
if @user.save
UserRegisteredMailerJob.perform_later(@user)
flash.notice = "User is successfully created."
redirect_to account_path
...
end

(追記)ActiveJob::Baseのサブクラスを定義することなく、

UserRegisteredMailerJob.perform_later(@user)

の部分を

UserMailer.registered(@user).deliver_later!(wait: 1.minute)

と直接メーラから#deliver_later!メソッドで遅延して送信するジョブを登録することもできるようです。


ジョブの実行

メールの送受信をテストする前に、mailcatcherと呼ばれるgemをセットアップします。

Gemfileに以下の記述を追加し、bundle installを実行します。

gem 'mailcatcher'

環境設定ファイルに以下のように記述します。

config/environments/development.rb

Rails.application.configure do

...
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = { address: 'localhost', port: 1025 }
...
end

以下のコマンドでmailcatcherを起動します。

$ mailcatcher

各種サーバーを起動します。(ログを参照するためにそれぞれターミナルの別タブで開きます)

Redisサーバーを以下のコマンドで起動します。

$ redis-server

Resqueのワーカーを以下のコマンドで起動します。

$ bundle exec rake resque:work

Resqueスケジューラを起動します。

$ rake environment resque:scheduler

最後にRailsサーバーを起動します。

$ rails server

ここでブラウザでユーザー登録のアプリケーションの操作を行います。

1分後に、Resqueスケジューラのログに

resque-scheduler: [INFO] 2014-10-09T22:53:15+09:00: Processing Delayed Items

mailcatcherのログに

==> SMTP: Received message from '<from@example.com>' (315 bytes)

と表示され、確かにジョブが実行されています。


まとめ

Rails 4.2に標準で含まれるActive Jobで簡単にジョブを登録して、バックグラウンドでスケジューリングしたジョブを実行することができるようになりました。


参考サイト