概要
- 2015/05のHerokuの料金体制改定で、Freeプランでもworker dynoが使えるようになった。
- さらに、2015.06にはHeroku Redisが一般公開された。25MBまでなら無料で使える。
- Rails 4.2で追加された
ActiveJob
の機構を使って、シンプルに、効率的に無料でHeroku上でバックグランド処理を実装できるようになった。
背景知識
Heroku
- Herokuにはweb dynoとworker dynoがある。
- HTTPリクエストを処理するのがweb dyno
- バックグランドで処理をするのがworker dyno
- 元々の無料プランではweb dynoの追加には料金がかかったが、新しい無料プランでは制限付きながらworker dynoが使えるようになった。実験するには、それでも十分。
- Heroku上でバックグランド処理をやる仕組みとしては
Delayed Job
というものが用意されていたが、 DBの負荷的な問題から現在は使用が推奨されていない。- 代わりにRedisベースのキューイングライブラリの使用が推奨されている。(
Resque
など)
- 代わりにRedisベースのキューイングライブラリの使用が推奨されている。(
Redis、Resque
Resque Scheduler
- Resque単体では、キューに積まれたジョブを順次実行していくことしかできないが、
Resque Scheduler
を使うと、任意のタイミングでジョブを実行できるようになる。 - 具体的には、「数日後にメールを送りたい」というようなジョブのスケジューリングが可能になる。
- ややこしくなるため、
Resque Scheduler
の説明はここでは割愛。
ActiveJob
- Rails 4.2で登場した、バックグラウンドジョブの実行機構。
- Delayed JobとResqueなどのように、さまざまなジョブ実行機能のAPIの違いを気にせずにバックグラウンドジョブの実装を記述できる。
ステップ
- 開発環境のセットアップ
- Herokuの設定
- ローカルで動かす
- Heroku上で動作確認
開発環境のセットアップ
Redisのインストール
brew install redis
必要に応じて、自動起動の設定を行う。(インストール後のメッセージに表示される)
開発時
Resqueの設定
gemの追加
gem 'resque'
gemを追加するだけでOK。追加したらbundle install
を実行する。
Resqueの設定
Resque.redis = Redis.new(:url => ENV['REDIS_URL'])
Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }
イニシャライザconfig/initializers/resque.rb
を作成して、接続先のRedisの設定と、ジョブ内でActiveRecordを使えるようにするための設定を行う。
Heroku RedisでRailsからRedisを使う場合は、url
にENV['REDIS_URL']
を指定する。(https://devcenter.heroku.com/articles/heroku-redis#connecting-in-ruby)
環境変数REDIS_URL
は、Heroku Redisアドオンを追加したタイミングでセットされる。当然、ローカルで実行した場合はurlが空となるので、Redisサーバーがデフォルト以外の場所で動いている場合は別途指定が必要。
fork後にActiveRecord::Base.establish_connection
を実行して、ActiveRecordが使えるように設定している。
ActiveJobでResqueを使うように設定
ActiveJob::Base.queue_adapter = :resque
同じく、ActiveJob
用のイニシャライザを作成し、その中でqueue_adapter
に:resque
を指定する。
Resque起動用タスクの追加
require 'resque/tasks'
Resqueの起動
TERM_CHILD=1 QUEUES=* rake environment resque:work
TERM_CHILD=1
をつけないと、WARNING: This way of doing signal handling is now deprecated.
という警告が表示される。これは、Resqueの元々の実装で、プロセスを終了させるためのシグナルを受け取った際の仕様に問題があったため。互換性の問題のためデフォルトの挙動は古い仕様のままになっているが、現在ではその仕様は非推奨となっているためこのメッセージが表示される。TERM_CHILD=1
をつけると一般的な、他のプログラムと同じ終了の挙動になり、警告も消える。
実装
Jobクラスを作る
rails g job my_job
class MyJob < ActiveJob::Base
queue_as :default
def perform(instance)
p "Process #{instance}"
end
end
非同期処理をキューに積む
MyJob.perform_later(instance)
Herokuの設定
Heroku Redisの追加
https://elements.heroku.com/addons/heroku-redis からプランを選択して追加する。25MBまでなら無料のHobbyプランが使える。Hobbyプランでは永続化はできないが、Resqueで使う分には大きな問題はない。(Redisが再起動した時にその時のキューの内容が消えてしまうという問題はあるかもしれない)
Worker用プロセスの設定
worker: TERM_CHILD=1 QUEUES=* rake environment resque:work
Resque用のRakeタスクをWorkerとして起動させるように設定。
Workerの有効化
最後に、ダッシュボード上からWorkerを有効化して完成。
結論
Herokuのプラン改定、Heroku Redisの登場、Active Jobの登場によって、Heroku上でのバックグラウンド処理の実装がとても身近になった。どんどん活用しよう。