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