[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その2[priority指定]

delayed_job v2.0.5 priorityとrun_atの指定方法





delayed_job v2.0.5の基本部分は以下の記事を参照してください。
[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その1


今回はv2.0.4以降から指定できるようになった(と思う)priorityとrun_atの指定方法を解説します。
非常に簡単に指定はできるのですが、動きにまだ癖があるので注意してください。


delayed_jobのpriority(優先度)の指定方法


ObjectName.delay(:priority => 1).classMethodName(args)

ここで、classMethodNameはObjectNameというクラスのクラスメソッドとします。
また、argsはそのメソッドの引数とし、引数が存在しない場合は不要です。


delayed_jobのrun_at(実行予定時刻)の指定方法


ObjectName.delay(:run_at => 5.minutes.from_now).classMethodName(args)

delayの引数としてrun_atを渡しています。上記指定でこのメソッドの実行時刻から5分後に実行してくださいという指定になります


delayed_jobのpriorityとrun_atの同時指定方法


ObjectName.delay(:priority => 1, :run_at => 5.minutes.from_now).classMethodName(args)

priorityの動作テスト


以下のようにサンプルコードを作成します。


class JobTest
def test02
Job01.delay.test02("base", Time.now)
101.times do |i|
Job01.delay(:priority => i%3).test02(i.to_s, Time.now, i)
end
Job01.delay.test02("base2", Time.now)
end
end

※サンプルの詳細は[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その1を参照してください。


Jobの実行制御をしている以下のソースに追加します。


ファイル名 : worker.rb
184行目 : reserve_and_run_one_job
def reserve_and_run_one_job
job = Delayed::Job.reserve(self)
#run(job) if job
if job
File.open("c:\jobresult.txt", "a") do |f|
f.puts job.id.to_s + " , " + job.priority.to_s + " , " + Time.now.strftime('%Y/%m/%d %H:%M:%S') + "\n"
end
run(job)
end
end

railsのホームディレクトリへ移動し、スクリプトコンソールで以下を実行します


ruby script\console
>> j = JobTest
>> j.test02

全部で103件のレコードが当路されます


以下のコマンドでdelayed_jobのworkerを起動させます


rake db:work RAILS_ENV=development

以下のように実行されます。※抜粋


[Worker(host:lenovo-01 pid:3456)] Job01.test02 completed after 0.0000
[Worker(host:lenovo-01 pid:3456)] 100 jobs processed at 58.8235 j/s, 0 failed ...
[Worker(host:lenovo-01 pid:3456)] Job01.test02 completed after 0.0100
[Worker(host:lenovo-01 pid:3456)] Job01.test02 completed after 0.0000
[Worker(host:lenovo-01 pid:3456)] Job01.test02 completed after 0.0100
[Worker(host:lenovo-01 pid:3456)] 3 jobs processed at 59.9989 j/s, 0 failed ...

1度に実行されるJobの数は100までとなっていますので、100 jobs processedと出た後に、3 jobs processedと表示されます


さて、worker.rbに追加したファイルの書き込みはどうなっているのでしょうか。
どこに出来たかわからなかったので、適当に検索して見つけました。
ファイルの中身は以下のようになっていました。※抜粋


1 , 0 , 2010/12/31 18:33:09
2 , 0 , 2010/12/31 18:33:09
3 , 1 , 2010/12/31 18:33:09
4 , 2 , 2010/12/31 18:33:09
5 , 0 , 2010/12/31 18:33:09

1列目がdelayed_jobsテーブルのidで2列目がpriorityです。
これを見るとわかりますが、delayed_jobにおいて
run_atの条件が満たされていれば、priorityは特に加味されずidの若い順に実行されることがわかります。


つまり、priorityが0のJobに重い処理が走るようになっているといくらpriorityを1にしても、
その処理が終わるまでは実行されないことになります。


では、どうすれば良いかというと
jobのworkerを複数起動し、それぞれに対して実行するpriorityを指定すればいいのです。


rake db:work RAILS_ENV=development MIN_PRIORITY=0 MAX_PRIORITY=0
rake db:work RAILS_ENV=development MIN_PRIORITY=1

上記のように2つのworkerを起動させ、priorityが0のjobを専用で処理するworkerと
priorityが1以上のjobを専用で処理するworkerに分けて起動させればいいと思います。
※priorityの付け方をうまく設計してください。


なおLinux系であれば以下のようにしてください。


RAILS_ENV=development script/delayed_job start --min-priority 0 --max-priority 0 -n 3
RAILS_ENV=development script/delayed_job start --min-priority 1 -n 3

workerの本数等は搭載されているメモリにあわせて適宜調整してください。


おまけ : jobの実行失敗をキャッチするには


delayed_jobのソースコード中で以下のTODO SOMETHINGの部分にキャッチする方法を記述することで
解決することが出来ると思います。


ファイル名 : worker.rb
144行目 : reshedule
def reschedule(job, time = nil)
if (job.attempts += 1) < max_attempts(job)
job.run_at = time || job.reschedule_at
job.unlock
job.save!
else
say "PERMANENTLY removing #{job.name} because of #{job.attempts} consecutive failures.", Logger::INFO
# TODO SOMETHING
... 略 ...

なお、先頭のifの部分では失敗したJobのリスケジュールを実行しておりますので、
この前の段階でキャッチして、監視用のファイルを生成するなどをしても良いかもしれません。
私はTODO SOMETHINGの箇所でエラーファイルを生成するようにして、
別の監視用プロセスがそのエラーファイルが見つかったらそのファイルの中身をメールに書き出して
送るようにしています。
※上記部分でメール送信まで実行しても良いのですが、処理に時間がかかるのも嫌だったので、
分離して作成しています。(shですけどね)





安くLinux環境を構築するならココ
スポンサーサイト

[Rails][非同期処理]delayed_job設定方法まとめ

Rails2におけるdelayed_jobの設定方法まとめ


適宜追加していきます。


解説記事



v2.0.5
[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その1
[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その2[priority指定]
v2.0.3
[Rails][非同期処理]delayed_job使い方
[Rails][非同期処理]delayed_job使い方 その2
[Rails][非同期処理]delayed_job使い方 その3


インストール


gemでインストールする場合


gem install delayed_job -v 2.0.5

pluginでインストールする場合


ruby script/plugin install git://github.com/collectiveidea/delayed_job.git -r v2.0

※要git
※Linux系の場合はrubyは不要


Railsプロジェクトの設定方法


ファイル名 : enviroment.rb
... 略 ...
Rails::Initializer.run do |config|
... 略 ...
config.gem 'delayed_job', :version => '~>2.0.4'
end

※gemでインストールした場合


ファイル名 : Rakefile
... 略 ...
begin
gem 'delayed_job', '~>2.0.4'
require 'delayed/tasks'
rescue LoadError
STDERR.puts "Run `rake gems:install` to install delayed_job"
end

末尾に入れておけばOK


delayed_job用のテーブル作成


ruby script/generate delayed_job
rake db:migrate RAILS_ENV=development

※1行目 : Linux系の場合はrubyは不要
※RAILS_ENVは必要に応じて変更する。


delayed_jobで登録されたJobを実行する方法


Windows系の場合


rake jobs:work RAILS_ENV=development

※RAILS_ENVの部分は適宜変更する
※停止する場合はCtrl + Cで消せる


Linux系の場合


RAILS_ENV=development script/delayed_job start

※停止する場合はstart部分をstopに変更して実行する
※Windows系の場合の方法でも問題ない


JobWorkerのパラメータ指定方法


Windows系の場合(rakeコマンドで実行する場合)
rake jobs:workは引数を3つとれます。


rake jobs:works RAILS_ENV=development MIN_PRIORITY=0 MAX_PRIORITY=1

上記指定でdevelopmentのpriorityが0以上1以下のJobのみを実行するworkerが生成されます。
MIN_PRIORITY=1とするとpriorityが1以上となります。


Linux系の場合


RAILS_ENV=development script/delayed_job --min-priority 1 --max-priority 2 -n 3 --pid-dir=/var/run-m --sleep-delay 30 run


  • --min-priority ... 最小優先度

  • --max-priority ... 最大優先度

  • -n ... Workerプロセスの数

  • --pid-dir ... PIDファイルの設置場所(デフォルトはRAILS_ROOT/tmp/pids

  • -m ... daemonsのmonitorプロセスを生成する場合は指定

  • --sleep-delay ... Jobが見つからなかった場合に次に実行されるまでのsleep時間

  • run ... start, stop, restart, runが指定できる
    runの場合はtopに表示されるようになる?

※gems\1.8\gems\delayed_job-2.0.5\lib\delayed以下のcommand.rbより抜粋。
※裏でdeamonsを利用しているので、正しく動作するかはテストするかソース見るしかない。


delayed_jobのjob登録時のデフォルト値


priority => 0
run_at => Time.now


delayed_jobのWorkerのデフォルト値



  • max_attempts(最大実行回数) ... 25

  • sleep_delay(スリープタイム) ... 5
    ※Jobが存在しない時にsleepする時間

  • max_run_time(最大実行時間) ... 4時間
    ※Jobがロックされてから4時間以上経過するとハングアップと判断される

なお、Jobの実行に失敗した場合は次回の実行時刻が自動的にスケジューリングされる。
その際の計算式は(attempts ** 4) + 5である。
=> 実行回数の4乗 + 5秒後
即ち1回失敗したら5秒後に実行
2回失敗したら21秒後に実行される。
※delayed_job-2.0.5\lib\delayed\backend\base.rbの83行目reschedule_atより





↑Rails3用にVPS借りちゃいました。まぁまぁ使いやすいですよ!

[Rails][非同期処理]delayed_job 2.0.5 詳細解説 - その1

delayed_job v2.0.5 delayメソッドの使い方 その1


v2.0.3とv2.0.4以上は若干使い方が異なるようなので動きを解析(?)してみました。
今回は基本的なところからやっていきます。


利用環境


Windows 7 Home Premium 64bit
ruby 1.8.6
MySQL 5.1.53
===== jem =====
delayed_job 2.0.5
rails 2.3.8

解析の対象 : delayメソッド


v2.0.3とv.2.0.4以上の違いは以前使用されていたsend_laterコマンドが非推奨となったことです。
今回はこのdelayコマンドの動作について解説します。


使い方の変化


v2.0.3


Object.send_layter(:method, :params)

v2.0.4以上


Object.delay.method(params)

[注意]ここでmethodはObjectのクラスメソッドでないといけません。


解説用サンプルコード


Railsプロジェクトを作成し、delayed_jobの設定(Railsアプリの初期設定,enviroment.rbへの追加とRakefileへの追加,delayed_jobsテーブルの作成)app/model以下へ以下のソースを設置します。


ファイル名 : job_test.rb
class JobTest
def test01
job01 = Job01.new
job01.delay.test01(Time.now)
end
def test02
Job01.delay.test02("1", Time.now)
end
end

ファイル名 : job01.rb
class Job01
def test01(run_at, priority=0)
filename = "job1.txt"
File.open(FILE_BASE + filename, 'w') do |f|
f.puts Job01.message("test01", run_at, priority)
end
end

def self.test02(name, run_at, priority=0)
filename = "job2_#{name}.txt"
File.open(FILE_BASE + filename, 'w') do |f|
f.puts Job01.message("self.test02", run_at, priority)
end
end

def self.message(method_name, run_at, priority)
message = "jobname : Job01 #{method_name}\n"
message << "priority : #{priority.to_s}\n"
message << "run_at : #{run_at.to_s}\n"
message << "now : #{Time.now.strftime('%Y/%m/%d %H:%M:%S')}\n"
end
end

ここで、FILE_BASEという定数はenviroment.rb内に以下のように記述してあります


ファイル名 : enviroment.rb
FILE_BASE="#{RAILS_ROOT}/tmp/files/"

また、tmp以下にfilesというディレクトリも作成します


サンプルの実行


設置できたら動作を確認するために、スクリプトコンソール上で実行します


スクリプトコンソールの起動(コマンドプロンプト等で実行する)
cd Railsのルートディレクトリ
ruby script\console


Loading development environment (Rails 2.3.8)
>> j = JobTest.new
>> j.test01
>> j.test02

※実行結果は除外してあります
job.test01およびtest02において#<Delayed::Backend::ActiveRecord::Job id: XX...略と
表示されていればOKです。


MySQLのコンソール上でJobが登録されていることを確認します。


mysql> select * from delayed_jobs \G
*************************** 1. row ***************************
id: 45
priority: 0
attempts: 0
handler: --- !ruby/struct:Delayed::PerformableMethod
object: !ruby/object:Job01 {}

method: :test01
args:
- 2010-12-30 01:41:41.280246 +09:00

last_error: NULL
run_at: 2010-12-29 16:41:41
locked_at: NULL
failed_at: NULL
locked_by: NULL
created_at: 2010-12-29 16:41:41
updated_at: 2010-12-29 16:41:41
*************************** 2. row ***************************
id: 46
priority: 0
attempts: 0
handler: --- !ruby/struct:Delayed::PerformableMethod
object: LOAD;Job01
method: :test02
args:
- "1"
- 2010-12-30 01:41:44.305419 +09:00

last_error: NULL
run_at: 2010-12-29 16:41:44
locked_at: NULL
failed_at: NULL
locked_by: NULL
created_at: 2010-12-29 16:41:44
updated_at: 2010-12-29 16:41:44
2 rows in set (0.00 sec)

Jobの実行


[注意]Rakefileにdelayed_job用の設定を追加し、rake jobs:***が実行できる環境である必要があります。


コマンドプロンプト上で以下を実行します


rake jobs:work

Worker起動後にNoMethodError: undefined method `test01'と表示されること
test02は正常に処理が終了し、tmp/files以下にtest02_1.txtというファイルが作成されていることを確認します。


解説


ポイント1
test01は、インスタンスメソッド
test02は、クラスメソッド
です。
delayed_jobのdelayメソッドを利用した非同期処理を行う際には、非同期処理するメソッドは必ずクラスメソッドでないといけない。
※過去に何度も書いていますが、嵌るポイントなので書いちゃいます。


ポイント2
test02の引数は最低2つ。
Job01.delay.test02(引数1,引数2)という形式で記述しています。
つまり、delayed_jobのdelayメソッドを利用した非同期処理を行う際の、非同期処理するメソッドの引数の形式に変更を加える必要はない
ということになります。


次回はpriorityとrun_atの使い方の解説をします。
全部終わったら、QA形式にまとめます。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。