sidekiqの使い方

  • 507
    いいね
  • 8
    コメント
この記事は最終更新日から1年以上が経過しています。

sidekiqresqueやdelayed_jobのような非同期実行を実現するgemです。
使い方はgithubのwikiかRAILSCASTSあたりを見れば分かりますが、Railsで使う場合について簡単に要約してみます。

準備

バックエンドにredisが必要です。
とりあえず試すのであれば、Macならbrew install redisでインストールされます。
本番ではwebサーバと別にredisサーバを用意するべきでしょう。

インストール

通常通りGemfileに書いてbundle installするだけです。

Gemfile
gem 'sidekiq'

設定

initializersに起動時の設定を書きます。

config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
    config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' }
end

Worker

非同期に実行するworkerをapp/workers以下に配置します。
workerにperformというメソッドを作成し、その中に実行されるべきロジックを書きます。
実際のロジックはモデルに記述した方がテストしやすいと思います。
引数にモデルのインスタンスを取ることもできますが、いちいちシリアライズされるためパフォーマンスが低下します。なるべくidを渡してWorker内でロードするようにした方が賢明でしょう。
sidekiq_optionsのqueueにはnamespaceを指定します。
未指定だとdefaultというnamespaceになります。
sidekiqを起動する際に必要になるので覚えておいて下さい。

app/workers/event_worker.rb
class EventWorker
  include Sidekiq::Worker
  sidekiq_options queue: :event

  def perform(id)
    @event = Event.find(id)
    @event.calculate_rank!
  end
end

キューに入れる

perform_asyncを実行するとworkerがキューに入り、非同期でperformメソッドが実行されます。
通常、コントローラから呼び出されると思います。

app/controllers/events_controller.rb
class EventsController
  def ranking
    EventWorker.perform_async @event.id
  end
end

perform_asyncの代わりにperform_inを使うと指定時間だけ待ってから実行されます。

app/controllers/events_controller.rb
class EventsController
  def ranking
    EventWorker.perform_in 1.hour, @event.id
  end
end

sidekiq起動

キューに入れただけではジョブは実行されません。
とりあえずCUIで実行するには以下のようにします。

bundle exec sidekiq -q default event

namespaceが複数ある場合は全て指定します。
指定しないとdefaultのキューのみが実行されます。
しばらく待ってキューが実行され、コンソールにログが流れれば成功です。

起動設定

毎回上記のオプションを入力するのは面倒なので、設定ファイルを作成します。

config/sidekiq.yml
:verbose: false
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
:concurrency:  25
:queues:
  - default
  - event

設定ファイルを読み込んで起動する場合、以下のようにします。

bundle exec sidekiq -C config/sidekiq.yml

デプロイ

sidekiqは一度起動すれば(落ちるまで)動き続けますが、注意するのはデプロイ時です。sidekiqがロードしているコードは更新されないので、再起動を忘れると思わぬバグが発生する可能性があります。
capistranoを使っている場合、デプロイと同時に起動・再起動ができます。

config/deploy.rb
require 'sidekiq/capistrano'

set :sidekiq_role, :web

roleはsidekiqを走らせるroleです。複数台でsidekiqを実行すると自動的にバランシングされるので、特別な理由がなければrailsを走らせているroleを指定するのが良いでしょう。
設定ファイルはデフォルトで上記のconfig/sidekiq.ymlが使われます。
これでcap deployを実行すれば自動的にsidekiqが起動・再起動します。

リトライ・エラー処理

sidekiqはperformメソッドが例外を返すと自動的にリトライします。
処理に失敗した場合にリトライさせたいなら、raiseすればいいわけです。
retryに数字を指定すると、その回数だけリトライして諦めます。

app/workers/event_worker.rb
  sidekiq_options queue: :event, retry: 5

デフォルトではリトライはリトライ回数の4乗+15秒間隔で行われます。
即ち15,16,31,96,271…秒の間隔を置きます。
変更する場合はsidekiq_retry_inを定義します。
詳しくはwikiを参照して下さい。

リトライさせたくない場合、retryに数字ではなくfalseを指定します。

app/workers/event_worker.rb
  sidekiq_options queue: :event, retry: false

ダッシュボード

sidekiqにはこんな感じのかっこいいダッシュボードがビルトインされています。
有効にするにはまずGemfileにsinatraを追加します。

Gemfile
gem 'sinatra', require: false

次にroutes.rbにルーティングを追加します。

config/routes.rb
require 'sidekiq/web'

MyApp::Application.routes.draw do
  mount Sidekiq::Web, at: "/sidekiq"
end

これで/sidekiqにアクセスすればダッシュボードが表示されます。
アクセス制限をかけたい時は以下のようにします。

config/routes.rb
require 'sidekiq/web'

MyApp::Application.routes.draw do
  authenticate :user, lambda { |u| u.admin? } do
    mount Sidekiq::Web => '/sidekiq'
  end
end

テスト

rspec-sidekiqを使うとrspecでテストが書けます。

Gemfile
group :test do
  gem 'rspec-sidekiq'
end
spec/workers/event_worker_spec.rb
require 'spec_helper'

describe EventWorker do
  let(:test_event) { create(:event) }

  before do
    subject.perform test_event.id
  end

  it "performでjobがqueueに入ること" do
    should be_processed_in(:event)
  end
end

processed_in?の引数にはworkerのsidekiq_optionsで指定したnamespaceが入ります。
詳しくはrspec_sidekiqのreadmeを参照して下さい。

まとめ

sidekiqは非常に簡単なので是非使ってみて下さい。
個人的にはdelayed_jobやresqueよりも分かりやすいと思います。

追伸

rails4でも支障なく動きます。