ちょっと話題の記事

[Ruby on Rails]Active Job – Sidekiqを使ってのJobの実行

2014.12.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

先日、Ruby on Rails 4.2がリリースされました。このリリースで新たに追加された機能にActive Jobがあります。今回はActive Jobについての簡単な説明と、実際に動かしてみたソースについて書きたいと思います。

Active Jobとは

Active Jobについて、Rails Guideより以下の文を引用します。

Active Job is a new framework in Rails 4.2. It is a common interface on top of queuing systems like Resque, Delayed Job, Sidekiq, and more.

Ruby on Rails 4.2 Release Notes

Active Jobは一言で表すと、「キューを実現するためのインターフェース」と言えると思います。あくまで「インターフェース」であるので、Active Job単体ではキューを実現することはできません。以下のキューイングシステムを呼び出すことで、キューを実現します。

  • Delayed Job
  • Qu
  • Que
  • queue_classic
  • Resque 1.x
  • Sidekiq
  • Sneakers
  • Sucker Punch

Active Jobの実装

では、実際にActive Jobを動かすソースについてです。タイトルに書いた通り、今回はSidekiqをキューイングシステムとして使用しました。以下、Active Jobを実行した際の手順とソースです。

1.プロジェクト作成

Rails 4.2を使用するため、Gemfileにバージョンを指定してbundle installを行います。

gem 'rails', '4.2.0'

$ rails new でプロジェクトを作成後、Gemfileにsidekiqを追加してbundle installします。

gem 'sidekiq'

尚、sidekiqはキューを実現するためRedisを使用するので、予めインストールしておいてください。

2.sidekiq.ymlの作成

sidekiqの設定を/config/sidekiq.ymlに記述しました。以下のようになります。

sidekiqの定義ファイル
:concurrency: 25
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:queues:
  - default

3.application.rb

Active Jobがsidekiqを使用することを、/config/application.rbに定義します。以下の行を追加します。

/config/application.rb
config.active_job.queue_adapter = :sidekiq

4.Jobの作成

Active Jobではキューに入れて実行したい処理を「Job」と呼んでいます。JobはRailsのGeneratorで作成することができます。今回は「MyJob」という名前でJobを作成しますが、Generatorは指定した名前に「Job」というプレフィックスをつけるため、以下のようなコマンドとなります。

$ bundle exec rails g job My

/app/jobs/my_job.rb が作成されるので、以下のように編集します。

/app/jobs/my_job.rb
class MyJob < ActiveJob::Base  
  queue_as :default

  def perform(message)
    Task.new.exec(message)
  end
end
[/ruby]
Jobは「ActiveJob::Base」を継承しています。このJobにて実行したい処理は「perform」メソッドとして実装します。今回は引数を受け取り、それを後述するTask.execに渡すだけを行っています。
</p>
<h3>4.Taskクラス</h3>
<p>
Jobから呼び出されるTaskクラスです。今回は/app/models/task.rbに記述しました。
<h5>/app/models/task.rb</h5>

class Task
  def exec(message)
    Rails.logger.info(message)
  end
end

見ての通り、引数で受け取ったメッセージをログに出力しています。

5.Jobの呼び出し

Jobの呼び出しはControllerから行うことにしました。

/app/controllers/start_controller.rb
class StartController < ApplicationController
  def run
    # 即時実行する場合
    MyJob.perform_later('Task exec.')

    # 時間を置いて実行する場合
    MyJob.set(wait: 10.second).perform_later('Task exec 1.')
    MyJob.set(wait: 10.second).perform_later('Task exec 2.')

    render :nothing => true
  end
end

Jobを実行する場合は、対象のJobクラスの「perform_later」メソッドを呼び出します。ソースに書いてある通り、引数を渡すことも可能です。「perform_later」メソッドが呼び出されると、Active JobはキューにJobを登録し、SidekiqがJobを実行します。またJobは即時に実行することも、時間を置いて実行することも可能です。

ルーティングの定義

上記のControllerを呼び出せるよう、/config/routes.rbに以下を記述します。

/config/routes.rb
post 'start/run'

Active Jobの実行

上記で実装したActive Jobを実行してみます。

まずはRedisを起動します。

$ redis-server

別のターミナルにて、sidekiqを起動します。

$ bundle exec sidekiq

また別のターミナルにて、Railsを起動します。

$ bundle exec rails s

Jobが起動し、Taskクラスから出力されるログが見れるよう、予めtailにてログファイルを監視しておきます。

tail -f log/development.log

curlにてControllerを呼び出します。

curl http://localhost:3000/start/run -X POST -d ''

curlコマンドの実行後、まずは即座に実行されるJobが起動し、以下がログに出力されます。

[ActiveJob] [MyJob] [0298f473-5bd7-4715-9c73-b64aef2a7e6f] Task exec.

しばらくした後、時間を置いて実行するよう指定したJobが起動し、以下がログに出力されます。

[ActiveJob] [MyJob] [dbb4bd39-62f8-459c-ae47-a88a07326ff6] Task exec 1.
(中略)
[ActiveJob] [MyJob] [b31c8007-40a8-4dfb-b7fd-503903f3f47f] Task exec 2.

まとめ

Active Jobを使った簡単なキューの実装でした。Active Jobを使うことで、どのキューイングシステムを使ってもソースに差が出にくくなります。

またActive Jobにはキューの優先付けや、CallBackなどの仕組みもあるようです。それらについても、今後書いていければと思います。

参考サイト

Active Job adapters
Ruby on Rails 4.2 Release Notes
Active Job Basics
Active Job -- Make work happen later