ひげろぐ

技術者として仕事人としての思うところや覚え書きやらです
Home      Profile      Works     
2008-04-11

BackgrounDRbでワーカーの自動起動を制御するメソッド

set_no_auto_load

ワーカーを作ってBackgrounDRbサーバを起動すると基本的にワーカーが自動的に起動して待機した状態になる。

必要がないのにずっとアイドリングしているのはリソースの無駄だ。
あるいは開発中のワーカーが勝手に起動してしまうのは邪魔だ。

そんなことが気になる場合にはワーカーのコード内でset_no_auto_loadを引数trueで呼び出す。
するとワーカーが勝手に起動しない。

class FooWorker < BackgrounDRb::MetaWorker
  set_worker_name :foo_worker
  set_no_auto_load true

  ...

これをtrueにしたりfalseにしたりしつつ、script/consoleなどから

MiddleMan.all_worker_info

で確認するとワーカーが起動していたりしていなかったりする様が確認できる。

ちなみに手動で新しいワーカーを起動するには以下のコードで。

MiddleMan.new_worker(:worker => :foo_worker)

new_workerの返値はworkerじゃないので注意。
起動したworkerを取得するにはMiddleMan.workerを使わないといけない。

参考

2008-04-08

BackgrounDRbのちょっとイマイチと思うところ

最近しばらく使っててイマイチと思ったところ。

開発時にサーバー再起動がめんどい

ワーカーのコード書き換えるたびにサーバー再起動しないといけないのでめんどい。

まあ開発の大部分はサーバーを動かさずにメソッドごとのユニットテストで仕上げていくので、めんどいのは最後の仕上げとか動作確認しつつの調整の時くらいなんだけど。
そんくらい我慢しやがれですか。そうですか。

しかしせめてrestartが欲しい。
自分で書きやがれですか。そうですか。

なんでBackgrounDRbのサーバーすぐ死んでしまうん?

MiddleMan.workerの引数を間違えてハッシュで与えたりとかして取得したワーカーで何かしようとするとあっさり死ぬ。
ワーカーが死ぬんじゃなくてBackgrounDRbサーバーが死ぬ。

例えば以下のコードで瞬殺。

worker = MiddleMan.worker(:worker => :hoge_worker)
worker.ask_status

これでエラー吐いて死亡。
ちなみにワーカー取得の正しいコードは

worker = MiddleMan.worker(:hoge_worker)

単に間違えるなって話ですか。
でも死んでしまうのはちょっとショックというか不安感をあおるなぁ。

あとサーバー再起動したときに再起動前に取得したワーカーを使っても同じように死亡。
サーバー再起動のタイミングとか環境によっては気を遣わないとだめっぽいですな。

もうちょっと不測の事態に強くなって欲しい。
さすがにワーカーのコードで何かエラーが起きただけで死んだりすることはないけど。

しかしながら

とまあこういった不満はちょっとあるけど、大旨は問題なく使ってます。
さらにバージョンが上がって練り上がってくるのが楽しみ。

2008-04-06

BackgrounDRbによるタスクのスケジューリング方法まとめ

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

BackgrounDRbのスケジューリング方法いろいろいじってみたのでそのまとめ。

以下の三種類がある。

  • タイマーによるスケジューラ
  • Cronスタイルのスケジューラ
  • Unixスタイルのスケジューラ

タイマーによるスケジューラ

ワーカーのコードに書くタイプのスケジューラ。
ワーカーのメソッドでタイマーを設定できる。

メソッド 説明
add_timer(n) 与えたブロックをn秒後に1回だけ実行
add_periodic_timer(n) 与えたブロックをn秒ごとに実行

createでadd_periodic_timerを呼び出せば定期的に繰り返すバッチ処理などを行うことが可能。
コードに書くだけで設定できるのでお手軽と言えばお手軽。

add_timerは1回だけ実行される。
create以外の場所でも使うことができるので、何らかのタスクを行った一定時間後に後始末処理を行うとかいった使い方ができそう。

サンプルコード – lib/workers/foo_worker.rb

タイマーでログにタスク実行の日時を書き込むだけのサンプル。

class FooWorker < BackgrounDRb::MetaWorker
  set_worker_name :foo_worker
  def create(args = nil)
    add_timer(10) do
      logger.info "#{Time.now} - oneshot"
    end

    add_periodic_timer(1) do
      hello("#{Time.now} - repeated")
    end
  end

  def hello(message)
    logger.info message
  end
end

Cronスタイルのスケジューラ

設定ファイルであるconfig/backgroundrb.ymlに設定を書くタイプのスケジューラ。
crontabに書く形式でスケジュールを指定できる。

「:schedules:」というセクションを設定ファイルに追加して定義する。
具体的にどう書くかは言葉での説明がしんどいのでサンプルコード参照。

サンプルコード – config/backgroundrb.yml
:backgroundrb:
  :port: 11006
  :ip: 0.0.0.0 

:schedules:
  :foo_worker:
    :hello:
      :trigger_args: 0 0 8 * * *
      :data: Hello from a cron style scheduler
    :good_night:
      :trigger_args: 0 0 22 * * *
      :data: Good night from a cron style scheduler at 22:00
    :good_night:
      :trigger_args: 0 0 20 * * *
      :data: Good night from a cron style scheduler at 20:00

ワーカー、タスク(メソッド)は複数書くことができる。
ただし同じタスクに関するスケジュール設定を複数書くことはできず、複数書くとエラーにはならないが一番最後に定義されたものだけが有効になる。
上の例だとgood_nightは20時タスクに実行される。

「:trigger_args:」にcrontabと同じ形でスケジュールを指定。
「:data:」はそのまま文字列としてタスクの引数になる。

Unixスタイルのスケジューラ

個人的にいまいちなじみがないけどUnixスタイルのスケジューラもある。
Cronスタイルのスケジューラと同じようにスケジューリングの設定をconfig/backgroundrb.ymlに書く。
設定ファイルにCronスタイルとUnixスタイルの書き方を共存させても問題はない。

:backgroundrb:
  :port: 11006
  :ip: 0.0.0.0 

:schedules:
  :foo_worker:
    :hello:
      :trigger_args:
        :start: <%= Time.now + 1.second %>
        :end: <%= Time.now + 10.minutes %>
        :repeat_interval: <%= 3.seconds %>
      :data: Hello from a unix style scheduler

「:start:」の時間が来ると繰り返しがスタートし「:repeat_interval:」ごとに実行される。
「:end:」の時間が来ると繰り返しは終了。
「:start:」と「:end:」の間の時間よりも「:repeat_interval:」が長いとタスクは一度も実行されずに繰り返しは終了する。

スケジュールの書き方の他はCronスタイルのスケジューラと同じ。

参考

近況

しばらくバイクで放浪してました。明石焼きとか赤福とか食ってきました。
それにしても天気予報でエイプリルフールとか勘弁していだだきたい。しかも自分の走ってた地域だけピンポイントでとか。

2008-03-22

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に関する記事をいくつか書いてます。

copyright brass.to | powered by WordPress ME