Rails+Sidetiqでお手軽定時処理 on Heroku(無料!)
Railsで定時処理をやろうと思って色々トライしました。Sidekiq+Sidetiqを使ってHerokuの無料分で動作させることができたのでその手順を紹介します。
Railsでの非同期処理
古いものから新しいものまでいろいろ選択肢があります。
そしてRails4.2で実装される(された)ActiveJobというアダプタのような仕組みがあります。
以前開発したのアプリケーションではResqueを使っていたことと、3つのgemのなかではSidekiqが一番新しいということでSidekiqを使いました。
Sidekiqでの定時処理
本来Sidekiqは非同期処理のためのgemなので、Sidekiq単体では定期的に処理を実行するような仕組みはありません。X時間後に実行とかはありますが。定時処理をするには別の方法と組み合わせる必要があります。
例えば次の2つのに代表されるgemは単体で定時処理を実現できるgemです。Sidekiqと組み合わせなくても定時処理はできますが、定期的に呼び出す処理の中でSidekiqにジョブをエンキューするという使い方もまあ可能です。
それに対して、Sidekiqに定時実行機能を追加するgemもあります。
この中では圧倒的にGitHubスター数の多いSidetiqを使ってみることにしました。
Prerequisites
- Herokuアカウント
- Rails環境
- Redis
RedisのインストールとHerokuアカウントが必要ですが、ここは省略します。
実装していく
SidekiqとSidetiqの準備
インストール
インストールはGemfileに書くだけでOK。generateとかはありません。
gem 'sidekiq' gem 'sidetiq'
$ bundle install
設定
config/initialiers/sidekiq.rb
にこんな内容を書きます。Herokuに関わる設定がありますが、それは後述します。
Sidekiq.configure_server do |config| if Rails.env.production? if ENV['REDISCLOUD_URL'] config.redis = { url: ENV['REDISCLOUD_URL'], namespace: 'sidekiq' } end else config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' } end end
ワーカーの定義
そしてapp/workers
ディレクトリ以下に適当なワーカーを作ります(例えばHardWorker
)。
class HardWorker include Sidekiq::Worker include Sidetiq::Schedulable recurrence backfill: true do daily end def perform puts 'Doing hard work' end end
recurrence
で頻度を指定し、perform
で処理を記述します。daily
とかmonthly
、もっと複雑な指定もOKです。
ローカルで実行
$ bundle exec rake sidekiq
と実行するとワーカープロセス立ち上がります。さっきはdaily
と書きましたが、初めてテストのときはminutely
とかにした上でperform
にも適当なputs
あたりを書いておいて確認するのが安全かも。
ここまででローカルでの実行はできました。
Web UIの追加
SidekiqはRailsアプリケーションにジョブの管理画面をマウントすることができるため、ジョブの状態を簡単に知ることができて便利です。
使うためにはまずSinatraをインストールして、routes.rb
にちょちょいと書き加えます。
gem 'sinatra', '>= 1.3.0', :require => nil
# config/routes.rb require "sidekiq/web" require "sidetiq/web" Sample::Application.routes.draw do mount Sidekiq::Web => '/sidekiq' end
これで例えばhttp://localhost:3000/sidekiq
とかで管理画面をみられます。
Herokuで実行する
Redisアドオンを追加
$ heroku addons:add rediscloud # アドオンを追加 $ heroku config:set REDIS_PROVIDER=REDISCLOUD_URL # 必要な環境変数を追加
Unicornを使う
Unicornを使ってゴニョゴニョすることで1dynoでSidekiqを実行することができます。というわけでGemfileにgem 'unicorn'
を追記します。
そして、Unicornの設定ファイルを作ります。
herokuでRailsをUnicornに乗せる設定 | Workabroad.jpを参考にした上で、Sidekiqをspawnで起動させます。
# config/unicorn.rb timeout 15 preload_app true worker_processes 3 # whatever you had in your unicorn.rb file @sidekiq_pid = nil before_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn master intercepting TERM and sending myself QUIT instead' Process.kill 'QUIT', Process.pid end defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! if ENV['RAILS_ENV'] == 'staging' # Sidekiq関連はここ!【更新あり】 @sidekiq_pid ||= spawn("bundle exec sidekiq -c 2") Rails.logger.info('Spawned sidekiq #{@sidekiq_pid}') end end after_fork do |server, worker| Signal.trap 'TERM' do puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' end defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection end
あとはこのconfigを使って起動するようにルートにProcfile
を作成してデプロイすればOKです。
# Procfile web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
本当に使う場合はWorker Dynoを立ち上げるべきでしょうが、ちょっとした確認などのためであれば1Dynoです。無料でやってしまいましょう。
Sidetiqによる実装は本当に楽で、この記事の手順を進めるのもUnicornの設定とかを書くほうが面倒なぐらいだと思います。Sidekiq+Sidetiqいいですね。以前使っていたResque+Clockworkよりも良さそうです。