Rails
Resque
ActiveJob

ActiveJobのアダプタとしてResqueを使ってみる

More than 3 years have passed since last update.


はじめに

Railsアプリで即時的にUXに影響を与えないようなデータの更新処理を行いたい場合にその処理を別プロセスで行うことでレスポンスタイムをできるだけ落とさないような実装をすることができます。

作ったサンプルはこちら。

https://github.com/iguchi1124/activejob-resque-example

今回は、ActiveJobを使ってページの閲覧数の加算処理を実装し、別プロセスで実行します。


Resque

Resque - GitHub

Resqueはrubyとredisで実装されたjob queueです。

導入するためには予めredisをインストールしておく必要があります。


実装

まずは例としてPostモデルとその画面をscaffoldを使って生成します。


必要なモデルと画面の実装

$ rails g scaffold post title:string body:text

次に、このテーブルにview_countという閲覧数を保持するためのカラムを追加します。

$ rails g migration AddViewCountToPosts view_count:integer

データベースを作成します。

$ rake db:create

$ rake db:migrate

これで必要な画面とテーブルは完成です。

では次に、resqueを導入します。


resqueの導入

Gemfileに追記する。


Gemfile

gem 'resque'


$ bundle install

config/application.rbでjob queueのアダプタをresqueに設定する。


config/application.rb

# 省略

module ActiveJobResqueExample
class Application < Rails::Application
# 省略
config.active_job.queue_adapter = :resque
end
end

resqueのinitializerをconfig/initializers/に作成する。


config/initializers/resque.rb

Resque.redis = 'localhost:6379'


lib/tasksにresque.rakeという名前でファイルを作成して編集する。


lib/tasks/resque.rake

require 'resque/tasks'

namespace :resque do
task setup: :environment do
Resque.before_fork = -> _job { ActiveRecord::Base.connection.disconnect! }
Resque.after_fork = -> _job { ActiveRecord::Base.establish_connection }
end
end


これで導入できた。実際にアプリケーションを起動するときは、rails sコマンドによって立ち上げるアプリケーションのプロセスだけでなく、redisやresqueのプロセスを別に立ち上げる必要がある。

job queueのアダプタを立ち上げたい場合は

$ redis-server

$ LOGGING=1 QUEUE=* rake resque:work

で立ち上げられる。

複数のプロセスをターミナルの同一のタブで管理したいときはforemanというgemが便利なので導入する。


foremanの導入

詳細は公式ページを参照。

同様にGemfileを編集してターミナルでbundle installと入力する。


Gemfile

gem 'foreman'


Procfileという名前のファイルをプロジェクトのルートディレクトリに作成する。


Procfile

web: bundle exec rails s -b 0.0.0.0

redis: redis-server
resque: LOGGING=1 QUEUE=* bundle exec rake resque:work

これで、下記コマンドでrailsアプリケーションを起動できる。

$ foreman start

次は実際にActiveJobを使って閲覧数をカウントしていくJobの実装をする。


ViewCounterJobの実装

scaffoldで生成した記事(Post)のshowアクションが呼ばれた時にその記事のview_countを1加算するようにします。

まずはgeneratorを使ってViewCounterJobを生成します。

$ rails g job ViewCounter

生成したJobを編集します。


app/jobs/view_counter_job.rb

class ViewCounterJob < ActiveJob::Base

queue_as :default

def perform(post)
post.view_count += 1
post.save
end
end


次に、controller側からこのjobを呼ぶように編集します。


app/controllers/posts_controller.rb

class PostsController < ApplicationController

before_action :set_post, only: [:show, :edit, :update, :destroy]

# 省略

# GET /posts/1
# GET /posts/1.json
def show
ViewCounterJob.perform_later(@post)
end

# 省略
end


.perform_laterを使うことで、job queueにview_countを加算する処理が追加されます。

その他のActiveJobの使い方は Active Job の基礎 を参照。


Rspecでテストを書く

rails guidesではテストの書き方についてこのように書かれている。

Testing Jobs

今回はqueueの状態は無視して、Jobの内容が正しく実行されるかテストする。


spec/jobs/view_counter_job_spec.rb

require 'rails_helper'

RSpec.describe ViewCounterJob, type: :job do
let(:post) { create(:post) }

## Factorygirlを使っていない場合:
# let(:post) { Post.create(title: 'str', body: 'txt') }

it 'increments view count successfully' do
expect {
ViewCounterJob.perform_now(post)
post.reload
}.to change(post, :view_count).by(1)
end
end


.perform_nowを使えば、queueに登録されずに即時に実行できるので、これを用いる。