Help us understand the problem. What is going on with this article?

Heroku + Redis + Resque + ActiveJobで手軽に無料でバックグラウンド処理

More than 3 years have passed since last update.

概要

  • 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、Resque

Resque Scheduler

  • Resque単体では、キューに積まれたジョブを順次実行していくことしかできないが、Resque Schedulerを使うと、任意のタイミングでジョブを実行できるようになる。
  • 具体的には、「数日後にメールを送りたい」というようなジョブのスケジューリングが可能になる。
  • ややこしくなるため、Resque Schedulerの説明はここでは割愛。

ActiveJob

  • Rails 4.2で登場した、バックグラウンドジョブの実行機構。
  • Delayed JobとResqueなどのように、さまざまなジョブ実行機能のAPIの違いを気にせずにバックグラウンドジョブの実装を記述できる。

ステップ

  1. 開発環境のセットアップ
  2. Herokuの設定
  3. ローカルで動かす
  4. Heroku上で動作確認

開発環境のセットアップ

Redisのインストール

brew install redis

必要に応じて、自動起動の設定を行う。(インストール後のメッセージに表示される)

開発時

Resqueの設定

gemの追加

Gemfile
gem 'resque'

gemを追加するだけでOK。追加したらbundle installを実行する。

Resqueの設定

config/initializers/resque.rb
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を使う場合は、urlENV['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を使うように設定

config/initializers/active_job.rb
ActiveJob::Base.queue_adapter = :resque

同じく、ActiveJob用のイニシャライザを作成し、その中でqueue_adapter:resqueを指定する。

Resque起動用タスクの追加

lib/tasks/resque.rake
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をつけると一般的な、他のプログラムと同じ終了の挙動になり、警告も消える。

http://hone.herokuapp.com/resque/2012/08/21/resque-signals.html

実装

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用プロセスの設定

Procfile
worker: TERM_CHILD=1 QUEUES=* rake environment resque:work

Resque用のRakeタスクをWorkerとして起動させるように設定。

Workerの有効化

スクリーンショット 2015-10-20 13.43.53.png

最後に、ダッシュボード上からWorkerを有効化して完成。

結論

Herokuのプラン改定、Heroku Redisの登場、Active Jobの登場によって、Heroku上でのバックグラウンド処理の実装がとても身近になった。どんどん活用しよう。

参考リンク

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away