A Day in the Life

Rails での非同期処理

Rails での非同期処理

半年ぐらい前に調べてて、エントリーにしようとしてたメモが出てきたのでペーストしとく。半年前なので今は違うかも。個人的には BackgroundFu でいいじゃん、という感じ。というか重いけどすぐユーザには view 結果を返したい場合は Thread でいいじゃん、という感じ。

Rails での非同期処理

Rails で別プロセスで非同期処理するには、BackgrounDRb と BackgroundFu の二つのプラグインを利用するのが主流である。

BackgrounDRb

その名の通り、裏側で動いている dRuby に処理を投げる。Rails 側からは MiddleMan という DRbObject へのディスパッチャを通し、そのままワーカーのメソッドを実行可能。

# モデルの after_create 等でメールを送信
MiddleMan.worker(:email_worker).send_mail(self)
# ruby のインスタンスを扱うようにも書ける
email_worker = MiddleMan.worker(:email_worker)
email_worker.send_mail(self)
email_worker.foobar()

またスケジューリング等も可能で、crontab ぽいかきかたで、数分に一度処理を行ったり、データ格納用の result という dRuby サービスを通しての情報のやりとりなども可能である。

ただ、BackgrounDRb が落ちていた場合は例外が発生するし、SPOF でもある。このへんは RingServerを BackgrounDRb で使えば解決しそうではあるが、現在は提供されてない。

  • 良いところ
    • dRuby!dRuby! なので worker を Ruby ぽく書ける
    • 細かい worker ごとのスケジューリングが可能
    • worker に便利なメソッドがいろいろ
    • Rails 以外でも使える
  • 悪いところ
    • 設定がいささか面倒
    • BackgrounDRb のプロセスが落ちていたときこまる。SPOF

BackgroundFu

BackgroundFu は、BackgrounDRb に比べると機能的には弱いが、別プロセスに非同期処理を簡単にやらせることに特化したプラグイン。Job モデルを通して処理を登録し、あとは BackgroundFu の daemon が5秒ごとに新規 job を処理するだけ。

Job.enqueue! EmailWorker, :send_mail, self.id # 引数は YAML にダンプされ、model のインスタンスを直接渡す等はできない

Job.enqueue! に呼び出すクラス、引数等を登録しているだけである。非常にシンプルで、DB 周りが冗長化されていれば SPOF にはならないし、もしBackgroundFu の daemon が死んでいたとしても、DB にキューは登録されているので、あとから BackgroundFu を立ち上げ直せば問題ない。

  • 良いところ
    • 手軽。実装も解りやすいので手をすぐに入れられる
  • 悪いところ
    • 細かいスケジューリング等は無い
    • プラグイン本体のテストが無い
Capistrano で BackgroundFu の restart

BackgrounDRb のほうもちょっと変更すればできるけど

namespace :fu do
  task :restart do
    stop
    start
  end
  task :start do
    run "RAILS_ENV=production ruby #{releases_path}/script/daemons start"
  end
  task :stop do
    run "RAILS_ENV=production ruby #{releases_path}/script/daemons stop"
  end
end
記事の一覧 >