BackgounDRbでRailsの非同期処理とかバッチ処理とか

2010年5月追記 今使うならdelayed_jobとかResqueとかがいいっぽいです。今動いてるものはまだしも今から作るものでBackgrounDrbはやめた方がたぶん幸せになれます。

BackgrounDRbは名前の通りバックグラウンドでの処理に便利なプラグイン。

2007年の末に1.0がリリースされていた。
現在のバージョンは1.0.3になっている。

BackgrounDRbを使うとバックグラウンドの処理をコントローラからキックしてスタートさせたり、BackgrounDRbの持つスケジューリング機能によって実行することができる。
つまり時間のかかる非同期処理やバッチ処理に使うことができる。
ロギングやステータス確認の仕組みも整っていて、バックグラウンド処理のフレームワークとしてなかなかいいかんじになっている。

1.0になって以前のバージョンとは互換性がなくなっている部分もあるので、アップグレードするには若干コードの修正が必要になるかもしれない。
またWindows上のサーバで使えなくなっているようだ。(対応の予定はあるそうだ)

以下にインストールから軽く使うまでをまとめてみた。

インストール

BackgrounDRbはchronicとpacketというgemに依存しているのでRailsのプラグインとして入れる前にそれらを入れる必要がある。

$ sudo gem install chronic packet

BackgrounDRbのインストールは次のコマンドで。

$ ruby script/plugin install http://svn.devjavu.com/backgroundrb/trunk

ちなみに古いバージョンが入っている場合は事前に消しておいた方がいい。

セットアップ

使い始める前に rake backgroundrb:setup でセットアップが必要。
rakeコマンドでセットアップすると必要なファイルやスクリプトがRAILS_ROOT以下に配置される。

$ rake backgroundrb:setup
(in /var/www/bgrb_sample)
Copying backgroundrb.yml config file to /var/www/bgrb_sample/config/backgroundrb.yml
Copying backgroundrb script to /var/www/bgrb_sample/script/backgroundrb
Creating /var/www/bgrb_sample/lib/workers
Copying Worker Test helper file /var/www/bgrb_sample/test/bdrb_test_helper.rb

古いバージョンを入れていた場合はこれらのファイルも消すか待避させておく必要がある。
特にbackgroundrb.ymlとscript/backgroundrbに関しては互換性がないので。

config/backgroundrb.yml – 設定ファイル

セットアップ直後のconfig/backgroundrb.ymlの内容は次の通り。

:backgroundrb:
  :port: 11006
  :ip: 0.0.0.0 

このファイルで動作環境(production,development,test)やログに関する設定、スケジューリングの定義などを行うことができる。
なお必要があればerbのテンプレートに書くのと同じ要領で変数の埋め込みが可能。

特にいじらないでも動かすだけなら動くのでここではこのままで行く。

ワーカー(worker)の作成

バックグラウンドで行いたい処理の内容はlib/workers以下のワーカーのスクリプトに書く。
コントローラやモデルと同じようにジェネレータでワーカーのひな形を作ることができる。

$ ruby script/generate worker Foo
exists  lib/workers/
create  lib/workers/foo_worker.rb 

ジェネレータで作られたワーカーの内容は以下のごとく。

class FooWorker < BackgrounDRb::MetaWorker
  set_worker_name :foo_worker
  def create(args = nil)
    # method gets called, when new instance of worker is created.
  end
end 

createメソッドの内容はワーカーのインスタンスが作成されたときに実行される。
それ以外にメソッドを定義していくと、コントローラやスケジューリングによってキックすることが可能なタスクになる。

とりあえずログに実行した時刻と渡されたメッセージを記録するだけのタスクを作ってみる。

class FooWorker < BackgrounDRb::MetaWorker
  set_worker_name :foo_worker
  def create(args = nil)
    # method gets called, when new instance of worker is created.
  end

  def bar(message = 'nothing')
    logger.info "#{Time.now} #{message}"
  end
end 

なおメソッドの引数はタスク呼び出し側の仕様でひとつしか与えられない。
複数の値をタスクに渡したい場合は引数をハッシュなどにするといいだろう。

BackgrounDRbサーバの起動

ワーカーができたらBackgrounDRbサーバを起動。
以前のバージョンではrakeで起動していたが、スクリプト直叩きになった。

$ ruby script/backgroundrb start

-eで環境を指定できる。(設定ファイルに書くこともできるが、ここで指定することもできる)

$ ruby script/backgroundrb start -e production

何も指定しないとdevelopmentで動く。

ちなみに起動時に定義されているすべてのワーカーのインスタンスがひとつずつ作られる。(これを抑制する方法はあるがとりあえず割愛)

コントローラからタスクをキック

いわゆる非同期処理的使い方。

MiddleManというプロキシ的なクラスを使ってワーカーのインスタンスを取得してタスクを呼び出す。
そこ中の人とか呼ばないように。

worker = MiddleMan.worker(:foo_worker)
worker.bar('Hello from some controller')

これを例えばコントローラのアクションに書いておくと、そのアクションが呼び出されたときに非同期でタスクが走り始める。
非同期なのでタスクの終了を待たずにアクションの処理は次の行以降へと進んでいく。

スケジューリングによってタスクをキック

こちらはいわゆるバッチ処理的使い方。

いくつか方法があるが、ここではCronっぽく設定ファイルに定義する方法で。
config/backgroundrb.ymlの末尾に以下の内容を追加する。

:schedules:
  :foo_worker:
    :bar:
      :trigger_args: * * * * * *
      :data: Hello from a cron style scheduler

trigger_argsにcrontabに書くのと同じ形式でスケジューリングする。
dataはタスクに渡る引数。

つまりこの場合はFooWorkerのbarメソッドが引数 “Hello from a cron style scheduler” で毎秒呼び出される。

BackgrounDRbサーバの停止

以上のお試しが終わったら一応停止しておく。
起動の仕方がわかれば停止の仕方も察しがつくと思うが、その通りstopで。

$ ruby script/backgroundrb stop

開発中でもタスクの内容を変更するとBackgrounDRbサーバを再起動しないと行けないのでちょっとめんどい。
その時restartみたいな気の利いたオプションは用意されてないのでstopしてからstartしないといけないのでさらにめんどい。

以上

とりあえず動かすまで。
その他諸々に関してはまた別途まとめる。かもしれない。

ところで自分の環境の問題かもしれないけど、タスク呼び出しの取りこぼしなどがある気がするので(BackgrounDRbサーバ起動途中やワーカー作り途中でタスクをキックすると無言でスルーしてくれるような?)安定を求める人はもうちょっと待つか、その対策をして使った方がいいかもです。

参考

追記

一応Railsアプリのバッチ処理に関する以前の記事もよろしければどうぞ。

追記2

このあとBackgrounDRbに関する記事をいくつか書いてます。

BackgounDRbでRailsの非同期処理とかバッチ処理とか” に対して5件のコメントがあります。

  1. ぱん より:

    http://gihyo.jp/dev/feature/01/ap4r
    AP4Rとの比較など、していただけると助かります。機会ありましたらぜひ。

  2. akahige より:

    今日ちょうどAP4Rに触っていたので恐ろしくピンポイントなコメントですなw

    AP4R少し試して私が感じた印象は以下のようなものです。

    * 複雑すぎる
    * その割にドキュメントが少ない
    * Rails2.0対応に不安がある

    最新のリリースを使ってもRails2.0の環境でうまく動きませんでした。CSRF対策のとこで引っかかってるぽいです。
    けっこういろいろ調べたり試したりしたんですがドキュメントが少なく、RDocやソースをたどる気力もなくギブアップなかんじです。

    メッセージキューサーバを使った非同期処理をするなら最近はどうもWorklingとかがシンプルかつ堅牢そうでよさげです。

    ちなみにBackgrounDrbに関しては堅牢性に疑問を持っています。
    バッチ処理にはスケジューリングの仕組みとか整っているので使いやすいですけどね。
    フロントエンドとからめた非同期処理をさせるのはちょっと怖いかもなぁと思ってます。
    あとやっぱちょっと複雑だし。でもAP4Rよりは複雑じゃないかな。

  3. ぱん より:

    >今日ちょうどAP4Rに触っていたので恐ろしくピンポイントなコメントですなw

    なんだか、akahigeさまのブログで、そろそろ書かれそうなオーラがでてましたのでww

    例えば、個人のスケジュール管理をするアプリを作るとして、
    指定の時間になったらアラートメールがとぶ。みたいな仕組みを作ろうとした場合、
    akahigeさまならどう実装されますか?
    お手すきのときに、ご意見くださいませ。

  4. akahige より:

    小規模なものだったらBackgrounDRb使って1分ごとにバッチを動かすなどして適当にやりますかね。
    大量のユーザーを抱えた大規模なシステムでは逆にどうやってるのか興味深いですね。

    オープンソースのスケジュール管理ソフトとかSNSとかの中身をのぞいてみたら面白いかも。

  5. ぱん より:

    大規模になったら、Railsと切り離して、直接DBたたいて処理するようなスクリプトをcronで動かしたりするんですかね。

    ありがとうございました。
    参考になります!

コメントは受け付けていません。