Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Sidekiq Enterpriseを使う

お久しぶりです。サーバーサイドエンジニアの花井です。

みなさんは非同期処理に何をお使いですか? ActiveJobでしょうか? Resqueでしょうか? Sidekiqでしょうか?

弊社では歴史的経緯から、上記全てのgemをプロダクトで使っていますが、 この度Sidekiq Enterpriseを導入して非同期処理の統一に着手しました。 Sidekiq Enterpriseの日本語記事があまりなかったので、利用の検討や実際に使う際の注意点などを紹介できればと思います。

What is Sidekiq

SidekiqはRuby製の非同期処理フレームワークです。 resqueを使うと、ジョブのリトライとユニーク性の担保のために自前で実装が必要でつらいので、sidekiqの採用に至りました。

また、非同期処理の流量を制限したい(特に外部サービスへの問い合わせが発生する所)という意図があり、Enterpriseを採用するに至りました。

今回は以下の2機能を使ったので、その設定方法を紹介します。

  • RATE LIMITING
  • PERIODIC JOBS

How to use

install

ここから申し込みをすると利用に必要なキーの情報が送られてきます。 Gemfileに指定された内容を追記して、$ bundle install します。

Enterpriseを入れるとき、同時にProのインストールもします。 Enterpriseを申し込むとProの機能も使えるようになるというのは、こういうカラクリのようです。

Implementation

Wikiが充実しているので、そちらを参考にすればほとんど迷うことなく設定できます。

ActicveJobなどと併用している場合、Sidekiq単体(include Sidekiq::WorkerしたWorker)に変更する必要があるようです(Wikiに例がなかったのと、resqueueからの置き換えだったこともあり、Sidekiq単体で使うように書き換えていますがもしかしたらできるかもしれません)。

RATE LIMITING

大量の子ワーカーを作るような非同期処理1つ1つの中で、APIを叩くようなケースでは、許容量を超えたリクエストをしてしまうことがあると思います。そんな時に指定した量を超えないように制限できる機能がRate Limitingです。 ※Redis 2.8以上が必要です。

弊社ではサブシステムから大量のアプリ内メッセージを送信する際に、APIサーバーへ負荷をかけないようにすることを目的として使っています。

特別な設定は必要なく、流量を制限したいWorkerに、以下のような設定を記述します。

class HogeWorker
  include Sidekiq::Worker
  DELIVER_LIMIT = Sidekiq::Limiter.concurrent('limitter_name', 50, wait_timeout: 5, lock_timeout: 30)

  def perform
    DELIVER_LIMIT.within_limit do
          # 必要な処理
    end
  end
end

Sidekiq::Limiter.concurrent には、リミッター名、同時実行数、ロック空き待ち時間(秒)、ロックを手放す時間(秒)を指定できます。 Web UIにタブが追加され、そこからどのくらいロック待ちが起きたかなどのメトリクスを確認することができますので、チューニングも簡単にできます。

PERIODIC JOBS

非同期処理の定期実行をcron形式で指定できます。Sidekiqを使う場合、sidekiq-cronを使う方も多いと思いますが、Enterpriseにすると標準装備されています。 弊社では集計処理など、ピークタイムを避けて深夜に行いたい処理の実行に活用しています。

/config/initializer/sidekiq.rb の中に以下のように実行を開始する時間と、対象のWorkerを指定します。 cronに設定する時間は内部的にTimeクラスで操作されるので、システムのタイムゾーンに合わせて記述する必要があります。resque-schedulerを使っているとタイムゾーンも指定できるので、システムのタイムゾーンと異なるタイムゾーンを扱っている場合は注意が必要です。

Sidekiq.configure_server do |config|
  config.periodic do |mgr|
    mgr.register('0 20 * * *', Hoge::HardWorker)
    mgr.register('0 21 * * *', FugaWorker)
    mgr.register('0 23 * * *', Piyo::PiyoWorker)
  end

まとめ

プッシュ通知の送信が重複しないように、次はUnique Jobsを利用予定です。 他にも色々な機能があるので、必要に応じて活用していきたいです。

一緒にプロダクトの改善をやってくれる仲間を募集しています!

おまけ

Enterpriseの効果かわかりませんが、設定中に相談のIssueを立ててたところ早めに返事がもらえました。 また、確認の過程で翻訳のIssueが見つかったらしく、コントリビュートもできました。