[Rails][Resque]hookメソッドの使い方 その2

Resqueでのhookメソッドの使い方 実践編


前回`after_enqueue`等のメソッドの使い方を説明しました。
今回はこれらを使った簡単なJob制御をやってみます


簡単なJobのロック制御の実装


やること: 同一のJobを同時に複数Enqueueされないことを保証するための制御
コードは簡単にやると以下のようになります。


# coding: utf-8

class FooJob
@queue = :default

def self.before_enqueue(id)
Resque.redis.setnx(lock_key(id), DateTime.now.to_i)
end

def self.before_perform(id)
Resque.redis.del(lock_key(id))
true
end

def self.perform(id)
puts "perform"
end

def self.lock_key(id)
'mylock:' + name + '_' + id.to_s
end
end

`before_enqueue` 解説


`before_enqueue`は`false`を返すように実装すると、JobがRedis上に保存されなくなります。
また、Redisのsetnxは該当のkeyが存在する場合は`false`を返し、存在しない場合は値をRedisに保存し、`true`を返す仕様です。


これを利用し、lock_keyメソッドで任意のロック用のキーを生成し、そのキーがRedisに存在しなければ
Enqueue成功、存在する場合はEnqueue失敗となるようにしています。


`before_perform` 解説


JobがQueueから実行状態へ遷移した際には、Enqueueを受け付けるようにしています。
そのためには、`Resque.redis.del lock_key(id)`を実行し、
Redis上から`lock_key`のキーを削除するようにしています。


実行例


Rails cでEnqueueしている例です。
enqueue直後に再度enqueueを実行した際に`nil`が返り、enqueueに失敗したことがわかります。


 Resque.enqueue(FooJob, 1)
=> true
Resque.enqueue(FooJob, 1)
=> nil

redis-cli上の値は以下のようになっています。


redis 127.0.0.1:6379> keys *
1) "resque:queue:default"
2) "resque:mylock:FooJob_1"
3) "resque:queues"
redis 127.0.0.1:6379> get resque:mylock:FooJob_1
"1360980565"

ResqueのWorkerが起動し、Jobが実行されれば再度Enqueueすることができるようになります。


同様の機能を持った(もちろん上記サンプルより高度だが)gemとして、resque-lockなどがあります。
これらを応用して、FooJobは同時に10個までしかEnqueueされないなどの制御も実装ができます。

スポンサーサイト

[Rails][Resque]hookメソッドの使い方 その1

Resqueでのhookの使い方


Resqueを使う上で、enqueueする前に何かしたいなーやpeformされる前に何かしたいなーということはないでしょうか?
プラグインも豊富にあるので、それを導入しても良いですが、ちょっとしたものであれば自分で書いてしまうのも手かも知れません。
今回は、久しぶりに役立つ(?)記事をちょろちょろっと


環境


Rails 3.2.11
Resque 1.23.0
Redis 2.6.7

様々なHookメソッド


Resqueは容易なプラグイン開発をサポートするために
各種hookメソッドもサポートしています。


before_enqueue ... Enqueueされる前に実行される
after_enqueue ... Enqueueされたあとに実行される
before_dequeue ... Dequeueされた前に実行される
after_dequeue ... Dequeueされたあとに実行される
before_perform ... Performが実行される前に実行される
after_perform ... Performが実行された後に実行される
around_perform ... Peformが実行される
on_failure ... Jobの実行に失敗した際に実行される

xxx_enqueueの実行タイミング


Resque.enqueueをすると、before_enqueue, after_enqueueが実行されるので、
確認してみます。


# coding: utf-8
class FooJob
@queue = :default
def self.perform(*args)
puts "perform"
end

def self.before_enqueue(*args)
puts "---- before enqueue ----"
end

def self.after_enqueue(*args)
puts "---- after enqueue ----"
end
end

`rails c`でEnqueueしてみます。


$ rails c
> Resque.enqueue(FooJob, 1)
---- before enqueue -----
---- after enqueue -----
=> true

before_enqueueについて


before_enqueueの中で`false`を返すことで、Enqueueさせないことも可能です。
以下のようにbefore_enqueueを書き換えます。


def self.before_enqueue(*args)
puts "---- before enqueue ----"
false
end

enqueueに失敗し、結果として`nil`が返ってきます。
特定条件の際にenqueueをさせない等で利用ができそうです。


$ rails c
> Resque.enqueue(FooJob, 1)
---- before_enqueue -----
=> nil

複数のxxx_enqueue


plugin開発用に、`before_enqueue_xxxx`や`after_enqueue_xxxx`もHookするように設計されています。
そのため、以下のように記述することも可能です。


# coding: utf-8
class FooJob
@queue = :default
def self.perform(*args)
puts "perform"
end

def self.before_enqueue_foo_job(*args)
puts "---- before enqueue foo job ----"
end

def self.before_enqueue(*args)
puts "---- before enqueue ----"
end

def self.after_enqueue(*args)
puts "---- after enqueue ----"
end

def self.after_enqueue_foo_job(*args)
puts "---- after enqueue foo job ----"
end
end

このJobをEnqueueすると以下のようになります。


> Resque.enqueue(FooJob, 1)
---- before enqueue ----
---- before enqueue foo job ----
---- after enqueue ----
---- after enqueue foo job ----
=> true

なお、`before_enqueue`が`false`を返した場合でも、他の`before_enqueue_xxxx`は実行されます。


xxx_performの実行タイミング


# coding: utf-8
class FooJob
@queue = :default
def self.perform(*args)
puts "perform"
end

def self.before_perform(*args)
puts "---- before perform ----"
end

def self.after_perform(*args)
puts "---- after perform ----"
end

def self.around_perform(*args)
puts "---- around perform ----"
yield
end
end

上記Jobを`rails c`等で投入しておきます。
コンソール上で以下をコマンドを実行し、Workerを起動します。
※Rakefileに`require 'resque/tasks'`を追加してください


$ rake environment resque:work "QUEUE=*"

少し待つとJobが実行され、以下のように表示されます。


---- before perform -----
---- around perform -----
perform
---- after perform -----

before_performについて


`before_enqueue`は`false`を返すことで、Enqueueを阻止することができましたが、
`before_perform`で処理を強制停止させるには、`Resque::Job::DontPerform`などをraiseしてあげる必要があります。


def self.before_perform(*args)
raise Resque::Job::DontPerform
end

上記のJobが実行されると、`Failed`に追加されます


around_performについて


実装こんな感じです
ブロックが渡されてきますので、必ず`yield`をしてあげてください。


xxx_dequeueの実行タイミング


使ったことがないのですが、以下のようにすることでQueueから指定したJobを削除することができるようです


Resque.dequeue(FooJob)

この際に自動的にHookされるメソッドが before_dequeue, after_dequeueです


on_failureの実行タイミング


Jobが失敗した際に、実行される処理です。
クリティカルなJobが失敗した際に、ここでHookして、メールを送る等の処理が可能です。
一番良く使われるのではないでしょうか?


# coding: utf-8

class FooJob
@queue = :default

def self.perform(*args)
raise StandardError.new
end

def self.on_failure_foo_job(*args)
puts "---- on_failure -----"
end
end

JobをEnqueueし、Workerが実行されると、以下が表示されます。


$ rake environment resque:work "QUEUE=*"             
---- on_failure -----

なお、`before_perform`で`Resque::Job::DontPerform`がraiseされた場合は、`on_failure`がHookされないようです。
`StandardError`等は`on_failure`がHookされます。


before_xxx, after_xxxの名前の付け方


プラグインを作る際に推奨されているのは、HOOKNAME_IDENTIFIERとなります。
上記の`on_failure_foo_job`のようにユニークな名前になるようにしておきましょう。


次回はHOOKを使った簡単なJobの制御をやってみます。

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