Heroku に 30 秒でレスポンスを返さないといけないルールがあったのを忘れていたので、
急遽 Rails アプリで時間がかかる処理を非同期にすることにした。
Rails で非同期というと Resque や Sidekiq が今のところ人気だけど、
今回は Rails 4.2 で追加予定の ActiveJob を使うことにした。
バックエンドには Sidekiq。
Redis をインストール
バックエンドに使う Sidekiq は Redis が必要なので、
Vagrant + Chef + Berkshelf で構築している開発環境にインストールする。
Berksfile に
cookbook "redisio"
を追加し
rm -rf cookbooks
berks vendor cookbooks
で Cookbook をインストール。
redisio のレシピを使うように Vagrantfile を
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision "chef_solo" do |chef|
chef.cookbooks_path = ["./chef/cookbooks", "./chef/site-cookbooks"]
chef.add_recipe "redisio::install"
chef.add_recipe "redisio::enable"
end
end
という風に修正したら、vagrant provision
で chef-solo を実行。
ActiveJob と Sidekiq をインストール
ここからは vagrant ssh
で開発環境に入っての作業。
Gemfile に
gem "activejob", "4.2.0.beta1", require: "active_job/railtie"
gem "sidekiq"
gem "sinatra"
を追加して bundle install
。
ActiveJob は gem を直接使う。
Rails 4.1 のアプリで使いたかったので、activesuport 4.2.0.beta2 が不要な、4.2.0.beta1 を使用。
active_job/railtie
を require しないと、ActiveJob が Rails に読み込まれなかった。
あとは config/environment/development.rb で ActiveJob の設定を記述。
バックエンドで Sidekiq を使うように指定する。
Rails.application.configure do
config.active_job.queue_adapter = :sidekiq
end
ローカルにインストールした Redis を使うので、Sidekiq の Redis 設定は省略可能。
Sidekiq の Web コンソールをマウント
ちゃんとジョブが実行されたか確認したいので、Sidekiq の Web コンソールをマウントする。
sinatra もインストールしたのは、これが理由。
Sidekiq の Web コンソールは Sinatra を別にインストールしないといけない。
config/routes.rb を次のように修正。
Rails.application.routes.draw do
mount Sidekiq::Web => "/sidekiq"
end
ジョブを作成
次のコマンドでジョブを生成。
bundle exec railg generate job calc_score
すると
class CalcScoreJob < ActiveJob::Base
def perform(*args)
end
end
みたいな雛形が app/jobs/calc_score_job.rb に生成されるので、
perform メソッドに非同期で実行したい処理を書く。
こんな感じ。
class CalcScoreJob < ActiveJob::Base
def perform(*args)
project_id = args[0]
project = Project.find(project_id)
project.calc_score
end
end
コントローラーでジョブを使う
コントローラーで作成したジョブを使ってみる。
ドキュメントには perform_later
を使うと書いてあったけど、
RubyGems.org からインストールした 4.2.0.beta1 にはまだ実装されていないので、NoMethodError。
代わりに古い API の enqueue
を呼び出す。
class ProjectsController < ApplicationController
def calc_score
CalcScoreJob.enqueue(@project.id)
respond_to do |format|
format.html { redirect_to @project }
end
end
end
ジョブを実行
Rails プロジェクト直下に、次の内容を書いた Procfile を作成する。
web: bundle exec unicorn -p $PORT -c config/unicorn.rb
worker: bundle exec sidekiq
Foreman を使って Rails サーバーと Sidekiq ワーカーを起動。
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
worker: bundle exec sidekiq
コントローラーのアクションを呼び出してジョブを実行したら、
Sidekiq の Web コンソールで確認してみる。
ブラウザで http://localhost:8080/sidekiq
にアクセスして
Processed の数が増えていれば、ActiveJob のバックエンドとして
Sidekiq がちゃんと動いたことになる。