Railsでバッググラウンドジョブを利用するための仕組みがActive Jobです。Active Jobを使えば、HTTPのメインスレッドでは応答を即座に返して、バックグラウンドで重い処理を実行できます。
ということで、早速設定します。Active Jobは基本的なバックグラウンドジョブを実行するための枠組みを提供するだけです。実際のバックグラウンドジョブを実行するバックエンドは設定で指定できます。バックエンドは複数あるようですが、sidekiqが現状のデファクトのようです。sidekiqはrailsとは別のプロセスで動作して、redisを介してジョブのやり取りを行います。簡易的なメッセージキューですね。
他にも有名なバックエンドにResqueというものものあるようですが、こちらもredisを介してジョブの実行を行いますが、redisが1.xでしかテストしてないとプロジェクトのページに書かれていたり、redisに対しては一定時間ごとにポーリングしてジョブの存在チェックをしているようです。ちょっと古臭いです。
インストール
redisとsidekiqをインストール・セットアップします。redisは、
$ brew install redis
で、インストールします。後は、
$ redis-server /usr/local/etc/redis.conf
でredisを起動します。
Rails側はGemfileに次の項目を追加して、インストールします
gem 'redis'
gem 'connection_pool'
gem 'sidekiq'
gem 'sinatra', require: false
config関係
前回の記事のように、redisの設定をします。
まずは、config/redis.ymlに次の記述をします
default: &default
host: localhost
port: 6379
development:
<<: *default
test:
<<: *default
production:
<<: *default
host: production-redis-server
次にsidekiqの設定を行うconfig/initializers/sidekiq.rbを次のようにします。
# Load the redis.yml configuration file
redis_config = YAML.load_file(Rails.root + 'config/redis.yml')[Rails.env]
redis_conn = proc {
Redis.new host: redis_config['host'], port: redis_config['port']
}
Sidekiq.configure_client do |config|
config.redis = ConnectionPool.new(size: 5, &redis_conn)
end
Sidekiq.configure_server do |config|
config.redis = ConnectionPool.new(size: 25, &redis_conn)
end
コネクションプールのサイズは決め打ちしていますが、redis.ymlで設定してもいいでしょう。
workerのファイルはapp/workersディレクトリに配置します。このディレクトリを自動で読み込むようにconfig/application.rbを設定します。
それと、バックエンドにsidekiqを使用するように設定します。
config.autoload_paths << "#{config.root}/app/workers"
config.active_job.queue_adapter = :sidekiq
起動
設定が終わったら、railsと一緒にsidekiqを立ち上げます。
$ bundle exec sidekiq
オプションでログファイルを指定したり、デーモン化できます。productionで動かす時は-e productionをオプションに追加します。
workerは変更しても自動では再読み込みされないので、プロセスの再起動が必要です。
workerの設定
ジョブはworkerで指定します。次のコードは標準出力に出力するworkerです。
class PrintWoker
include Sidekiq::Worker
def perform(s)
print s
end
end
引数で与えられた文字列を出力するだけです。workerの中ではActive Recordなども普通に扱えます。
ではworkerを実行させるためのrails側のコードです。controllerなどに次のように記述します。
PrintWorker.perform_async "Hello, World"
これだけで非同期にworkerが実行されます。performを直接実行すると、メインスレッドの中でそのまま実行できます。引数には文字列や数字などのプリミティブなものだけしか扱えないので、注意が必要です。
これ以外にも、指定した時間にジョブを実行させることや、数分後に実行など、ジョブを実行させるタイミングも制御できます。
ActionMailer
ActionMailerもActive Jobに対応しています。deliver_nowを実行していたものをdeliver_laterに変更するとバックグラウンドジョブとして非同期に実行されます。
sidekiqはデフォルトではdefaultのキューしか処理ません。メールキューを処理させるためには、次のコマンドで起動します。
$ bundle exec sidekiq -q default -q mailers