(2009年版)backgrounDRbについて調査してみた
ruby on railsでバックグラウンド処理を行うためのプラグイン「backgrounDRb」が使いたくて調べてみました。
このプラグインは結構前から存在しているみたいでWeb上でも情報はたくさん存在しているんですが「backgrounDRb」自体のバージョンアップに伴う仕様変更が大きいみたいで昔の情報だとうまく行かないパターンが多く苦労しました。
今回はハマったポイントをふまえつつ、「backgrounDRb」についてまとめてみたいと思います。
backgrounDRbとは
ruby on rails上でバックグラウンド処理を行うためのプラグインです。例えば、web上から何かの操作をした際にメールを送りたいことがありますよね?このときに添付ファイル付きの大きなメールだと送信が完了するまで画面遷移は発生せず「待ち」の状態となってしまいます。backgrounDRbを使用すると「メールを出してね」という指示さえ発行すればweb上からはすぐに処理が完了したように見えます。backgrounDRbは指示された処理をバックグラウンドで淡々と順番にこなしていくだけです。
また、backgrounDRbは定期的に実行するプログラムもスケジューリングさせることができます。
インストール
使用するためにはchronicとpacketをgemでインストールしてください。
gem install chronic packet
プロジェクトをまだ作成していないようであれば先に作成しておきましょう。
ここではtestプロジェクトとします。
rails test
プロジェクトを作成したらpluginとしてbackgrounDRbをインストールしましょう。
ruby script/plugin install http://svn.devjavu.com/backgroundrb/trunk
インストールが完了したらセットアップを行います。
rake backgroundrb:setup
これで準備完了です!
動作確認
backgrounDRbを起動します。
ruby script/backgroundrb start
railsのコンソールを起動します。
ruby script/console
起動しているworkerを確認します。
MiddleMan.all_worker_info
MiddleMan.query_all_workersではありません。(その情報は古いです)
うまく起動してたら
>> MiddleMan.all_worker_info => {"0.0.0.0:11006"=>[{:status=>:running, :worker_key=>"", :worker=>:log_worker}]}
という感じに表示されます。
ちなみにbackgrounDRbが起動していなければ
>> MiddleMan.all_worker_info => {"0.0.0.0:11006"=>nil}
という感じになります。インストール方法や起動コマンドをもう一度確認してみてください。
テストプログラム
ここでは画面をクリックするたびにログに出力するプログラムを作成してみます。
まずはbackgrounDRbのworkerというものを作成します。
ruby script/generate worker printlog
実際にlib/workers以下にprintlog_worker.rbが作成されているのが確認できると思います。
printlog_workerの中身を作成しましょう。
class PrintlogWorker < BackgrounDRb::MetaWorker set_worker_name :printlog_worker def create(args = nil) # this method is called, when worker is loaded for the first time end #ここに重い処理を記述する。 #引数は1つしか設定できないので複数の引数を渡したいときはhash等に詰めて渡すこと def writer(arg) sleep 20 msg = arg[:msg] name = arg[:name] logger.debug(msg+':'+name+':'+Date.today.to_s('yyyy-mm-dd')+Time.now.to_s) end end
続いてPrintLogWorkerを呼び出すためのcontrollerを作成しましょう。
ruby script/generate controller logwrite
中身はこんな感じです。
class LogwriteController < ApplicationController def write MiddleMan.worker(:printlog_worker).async_writer(:arg => {:msg => 'log',:name => 'Kazuhisa'}) redirect_to(:action => 'index') end end
async_writerという感じにworkerで指定したアクション名の頭にasyncをつけると非同期で実行されます。asyncなしでアクション名を呼び出すと、backgrounDBr経由で実行しない場合と同じくフツーに待たされます。
さてwriteアクションを呼び出すためのviewを作成しましょう。
・app/views/logwrite/index.html.erb
<%= link_to('WriteLog', :controller => 'Logwrite', :action => 'write') %>
実行結果
ruby script/server
で実行して、画面に表示されている「WriteLog」をクリックしまくってみましょう。
backgrounDRbで出力したログはlog/backgroundrb_XXXXX.logに出力されます。
kazuhisa-mac:log kazuhisa$ tail -f backgroundrb_11006.log # Logfile created on Sun Aug 16 22:28:22 +0900 2009 by / log:Kazuhisa:2009-08-16Sun Aug 16 22:29:21 +0900 2009 log:Kazuhisa:2009-08-16Sun Aug 16 22:29:41 +0900 2009
20秒ごとに出力されているのが分かりますね。
うまく実行されていない場合の注意点
ログ(log/backgroundrb_XXXXX.log)を見てみましょう。worker内のエラーはここに出力されているはずです。railsのconsoleで動作を確認してみるのもいいかもしれません。
あと、log用のworkerは元々起動しているので
ruby script/generate worker log
とかはやめましょう。私はこれでかなり悩みました。
それからworkerを修正した場合はbackgrounDRbを必ず再起動しましょう。restartオプションは無いのでstopしてstartです(笑)。
困ったら公式マニュアルも役に立ちます。
http://backgroundrb.rubyforge.org/manual/index.html