Rails
shoryuken

shoryukenの諸動作

More than 3 years have passed since last update.

Railsの非同期処理として、今までDelayedJobで運用していたものを、shoryukenに載せ替えたので、使い勝手をまとめます。

最初はsidekiqにしようと思っていたので、ちょいちょいsidekiqとの比較が出てきます。


メッセージのポーリング

メッセージのポーリング間隔はshoryuken.ymlで設定します。ちなみに、ここのポーリング間隔と、エンキューされるキューの合計数でSQSの料金が変わってくるみたい。ただ、SQSの料金プランによると100万件ごとに課金されるので、そこまでシビアに考える必要はなさそうだけれど。


shoryuken.yml

:delay: 25  # 単位は秒


以下のようにしておくと、リクエストを投げ続けてくれる。


shoryuken.yml

:delay: 0


ちなみに、このdelay値は、エンキューされてから、処理が開始されるまでの時間にモロに響いてくるので、もし「即時送らねば!」という処理を積みたい場合は、金額を試算した上で短くすることをおすすめします。


リトライ


リトライ機構

sidekiqではsidekiq.ymlに


sidekiq.yml

:retry: 25


と書いておけば、リトライ回数を設定できたが、shoryukenではそういうわけにはいかない。

重要となるのは、SQSのVisibility Timeoutです。

SQSに蓄積されたメッセージは、蓄積直後、どのshoryukenワーカーからも見える状態になっています。

そして、1つのワーカーがメッセージを受信した時点で、そのメッセージは見えなくなります(処理中という扱い)。

しかし、処理が一定時間終わらなければ、SQS側では「なにか失敗したかな?」ということで、メッセージを再びどのワーカーからも見える状態に戻します。この処理中扱いにしてくれる時間が、Visibility Timeoutです。

処理の成功・失敗にかかわらず、Visibility Timeoutが来てしまえば、再びメッセージが受信可能な状態になり、いずれかのワーカーに処理される可能性があります。


成功時

処理の成功・失敗にかかわらず、Visibility Timeoutが来ればメッセージが受信可能状態になる、と言いましたが、shoryukenにはauto_delete というオプションがあり、これが成功時にメッセージを削除してくれます。

なので、auto_deleteを有効にしておけば、成功したメッセージが再び処理されることはなくなります。


sample_worker.rb

class SampleWorker

include Shoryuken:Worker
shoryuken_options queue: "default", auto_delete: true

def perform(sqs_msg, body)
# something
end
end


auto_delete: false はどんなときに使うのか、イマイチ想像できないですね……。


リトライ回数指定

というわけで、失敗時には、メッセージが削除されず、Visibility Timeoutが来た後再びどこかのワーカーが受信し、処理します。

では、このリトライ回数に上限は指定できるのでしょうか?

これは、shoryuken側の設定ではなく、SQS側で設定します。

SQSにはDead Letter Queueというものが設定できます。これは、「n回受信(ワーカーがメッセージを取得)したらDead Letter Queueとして指定された別のキューにメッセージを移す」というものです。

Dead Letter Queueに入ってしまえば、キューが変わるので、shoryukenのワーカーからはまったく見えなくなります。

なので、リトライ回数の指定は、Dead Letter Queueの受信回数条件で設定することが可能です。


shoryuken.yml

:queues:

- asumibot-patient-queue

みたいなキュー設定をしたら、AWS SQSにDead Letter Queueを作ります。

新しく、asumibot-dead-letter-queue というキューを作り、asumibot-patient-queue の設定画面で、

スクリーンショット 2016-02-08 14.40.33.png

こんな設定をしてやれば、20回目の受信で、Dead Letter Queueに移してくれて、それ以降リトライされなくなります。

リトライしたくない場合は、ここのMaximum Receives を1にしておけば良さそう。


リトライ間隔

リトライ間隔は以下のように、ワーカーごとに設定できます。


sample_worker.rb

class CopyCheckWorker

include Shoryuken::Worker
shoryuken_options queue: "default", auto_delete: true, retry_intervals: [60, 120, 180] # 単位は秒

この状態だと、1回目のリトライが60秒後、2回目が120秒後となります。

ただし、retry_intervals で設定した値を超えた回数リトライが発生した場合は、通常通りVisibility Timeoutに依存したリトライとなります。

また、ここで指定するのはあくまで間隔だけで、リトライ上限回数はDead Letter Queueでしか設定できませんでした。

※ただし、retry_intervals はこの時間後にメッセージが見えるようになるだけで、厳密にはポーリング間隔との兼ね合いがあり、ポーリング間隔が長い場合は即時受信してくれるわけではない。


Visibility Timeoutの変更

Visibility Timeoutがかなり重要なことはご理解いただけたと思います。

このVisibility Timeout、デフォルトでは30秒となっています。

もし、ジョブの実行時間が30秒をオーバーしそうな場合には、Visibility Timeoutを延ばす必要がでてきます。


sample_worker.rb

class SampleWorker

include Shoryuken::Worker
shoryuken_options queue: "default", auto_delete: true, retry_intervals: [60, 120, 180]

def perfom(sqs_msg, body)
sqs_msg.visibility_timeout = 60
# something
end
end



スレッド

shoryukenのスレッドは、shoryuken.ymlのconcurrency で設定します。


shoryuken.yml

:concurrency: 25


キューごとのスレッド数を指定したい場合は、


shoryuken.yml

:concurrency: 25

:queues:
- [ default, 4 ]
- [ asumiss, 10 ]

と書いておき、合計値がconcurrency を超えないようにしておきます。

この辺はsidekiqと同じですね。


番外編:fake_sqs

ローカルで以上のようなshoryukenの動作を実現したい場合、AWS SQSではなくfake_sqs を使うという手があります(安いとはいえAWS SQSではお金かかりますからね)。

http://qiita.com/iemon7stars/items/d4efdd8872d287906d29

fake_sqsの場合、ほとんどAWS SQSと同等の動作をしてくれますが、2点重要なことがあります。


  1. メモリ上にキューが作られるので、fake_sqsを起動するたびにキューを作ってやる必要がある

  2. retry_intervalsの設定が効かない

1は、まぁ仕組み上仕方ないでしょう。

http://qiita.com/iemon7stars/items/d4efdd8872d287906d29

こちらの記事通り、どこかでキューを作ってやるしかないです。

2については、shoryukenのmiddlewareで失敗時に、sqs_message.attributes['ApproximateReceiveCount'] からattempts (失敗回数)を導き出して失敗後の処理をしていますが、fake_sqsを見る所attributesが空になっているのが原因です。

おそらく、fake_sqsではメッセージのattributesに対応していないので、ここはどうにもならないですね……。