Railsの非同期処理として、今までDelayedJobで運用していたものを、shoryukenに載せ替えたので、使い勝手をまとめます。
最初はsidekiqにしようと思っていたので、ちょいちょいsidekiqとの比較が出てきます。
メッセージのポーリング
メッセージのポーリング間隔はshoryuken.ymlで設定します。ちなみに、ここのポーリング間隔と、エンキューされるキューの合計数でSQSの料金が変わってくるみたい。ただ、SQSの料金プランによると100万件ごとに課金されるので、そこまでシビアに考える必要はなさそうだけれど。
:delay: 25 # 単位は秒
以下のようにしておくと、リクエストを投げ続けてくれる。
:delay: 0
ちなみに、このdelay値は、エンキューされてから、処理が開始されるまでの時間にモロに響いてくるので、もし「即時送らねば!」という処理を積みたい場合は、金額を試算した上で短くすることをおすすめします。
リトライ
リトライ機構
sidekiqではsidekiq.ymlに
:retry: 25
と書いておけば、リトライ回数を設定できたが、shoryukenではそういうわけにはいかない。
重要となるのは、SQSのVisibility Timeoutです。
SQSに蓄積されたメッセージは、蓄積直後、どのshoryukenワーカーからも見える状態になっています。
そして、1つのワーカーがメッセージを受信した時点で、そのメッセージは見えなくなります(処理中という扱い)。
しかし、処理が一定時間終わらなければ、SQS側では「なにか失敗したかな?」ということで、メッセージを再びどのワーカーからも見える状態に戻します。この処理中扱いにしてくれる時間が、Visibility Timeoutです。
処理の成功・失敗にかかわらず、Visibility Timeoutが来てしまえば、再びメッセージが受信可能な状態になり、いずれかのワーカーに処理される可能性があります。
成功時
処理の成功・失敗にかかわらず、Visibility Timeoutが来ればメッセージが受信可能状態になる、と言いましたが、shoryukenにはauto_delete
というオプションがあり、これが成功時にメッセージを削除してくれます。
なので、auto_delete
を有効にしておけば、成功したメッセージが再び処理されることはなくなります。
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の受信回数条件で設定することが可能です。
:queues:
- asumibot-patient-queue
みたいなキュー設定をしたら、AWS SQSにDead Letter Queueを作ります。
新しく、asumibot-dead-letter-queue
というキューを作り、asumibot-patient-queue
の設定画面で、
こんな設定をしてやれば、20回目の受信で、Dead Letter Queueに移してくれて、それ以降リトライされなくなります。
リトライしたくない場合は、ここのMaximum Receives
を1にしておけば良さそう。
リトライ間隔
リトライ間隔は以下のように、ワーカーごとに設定できます。
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を延ばす必要がでてきます。
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
で設定します。
:concurrency: 25
キューごとのスレッド数を指定したい場合は、
: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点重要なことがあります。
- メモリ上にキューが作られるので、fake_sqsを起動するたびにキューを作ってやる必要がある
- retry_intervalsの設定が効かない
1は、まぁ仕組み上仕方ないでしょう。
http://qiita.com/iemon7stars/items/d4efdd8872d287906d29
こちらの記事通り、どこかでキューを作ってやるしかないです。
2については、shoryukenのmiddlewareで失敗時に、sqs_message.attributes['ApproximateReceiveCount']
からattempts
(失敗回数)を導き出して失敗後の処理をしていますが、fake_sqsを見る所attributesが空になっているのが原因です。
おそらく、fake_sqsではメッセージのattributesに対応していないので、ここはどうにもならないですね……。