Amazon SQSのDead Letter Queueにメッセージが移動するタイミングを調べてみた

2016.02.22

サーモン大好き、横山です。

今回はAmazon SQSの Dead Letter Queue にメッセージが移動するタイミングを調べてみました。

Queueの作成

対象となる通常のQueueと Dead Letter Queue を入れるQueueを作成します。

「新しいキューの作成」をクリックします。

sqs_dlq_01

先に Dead Letter Queue を入れるQueueを作成していきます。 「キュー名」を demo_dlq と入力し、他はデフォルト値で「キューの作成」をクリック。

パラメータ名
キュー名 demo_dlq

sqs_dlq_02

Dead Letter Queue を作成後は通常のQueueを以下のパラーメータに従って作成します。 書いていない部分はデフォルトで作成します。

パラメータ名
キュー名 demo
再処理ポリシーの使用 レ (チェックを入れる)
デッドレターキュー demo_dlq
最大受信数 1

sqs_dlq_03

作成しましたら、それぞれのQueueURLをメモっておきます。

キュー名 URL
demo https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo
demo_dlq https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo_dlq

sqs_dlq_04

次にQueueにデータをsend_message、receive_message、するプログラムをPythonで書いていきます。

前提

今回はMac上で試しますが、AWS CLIの aws configure で、アクセスキー、シークレットキー、デフォルトリージョンが設定されているものとして作業を進めていきます。

環境構築

いつもの virtualenv を作成し、パッケージを boto3click というモジュールを入れます。 (参考: Welcome to the Click Documentation — Click Documentation (5.0)

$ virtualenv venv
$ . venv/bin/activate
(venv)$ pip install -U pip
(venv)$ pip install boto3 click

ソースコード (run.py)

#!/usr/bin/env python
# coding: utf8

# run.py

import boto3
import click
import pprint

def init_sqs():
return boto3.client('sqs')

@click.group()
def _():
pass

@_.command()
@click.argument('queue_url')
def send(queue_url):
sqs = init_sqs()
params = {
'QueueUrl': queue_url,
'MessageBody': 'send message',
}

resp = sqs.send_message(**params)
pprint.pprint(resp, indent=4)

@_.command()
@click.argument('queue_url')
def recv(queue_url):
sqs = init_sqs()
params = {
'QueueUrl': queue_url,
'AttributeNames': ['ApproximateReceiveCount'],
}

resp = sqs.receive_message(**params)
pprint.pprint(resp, indent=4)

if __name__ == '__main__':
_()

単純に、sqs send_messagesqs receive_message をPythonで実行するコードです。 send_messageは、QueueUrlMessageBody を、 receive_messageは、 QueueUrlApproximateReceiveCount を取得するために AttributeNames を設定してAPIを投げるようにしています。

ちなみに、ApproximateReceiveCount はQueueがメッセージを返した回数となっています。 (参考: ReceiveMessage - Amazon Simple Queue Service)

Queueにメッセージを投げる時は

(venv)$ python run.py send [queue_url]

Queueからメッセージを取得する時は

(venv)$ python run.py recv [queue_url]

と実行することで、取得出来ます。

検証

まずはじめに初期状態は以下の画像の様になっています。 demoキュー、demo_dlqキュー両方共空の状態です。

sqs_dlq_05

demoキューにsend_messageを投げる

(venv)$ python run.py send https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo
{ u'MD5OfMessageBody': 'da29a3de5fd13de436de4f0dd4165206',
u'MessageId': 'd31731b4-87dc-4298-bb18-a6e7c59f8603',
'ResponseMetadata': { 'HTTPStatusCode': 200,
'RequestId': 'beb23792-eaac-5c98-9ce7-78d634b23260'}}

demoキューにmessageを投げますと、demoキューが以下の様に1つメッセージを保持します。 確認するときに、画面の数字が変わらないとかありましたら、マネージドコンソールの右上の更新ボタンを押してください。

sqs_dlq_06

demoキューからreceive_messageを使い、Queueを取得する

(venv)$ python run.py recv https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo
{ u'Messages': [ { u'Attributes': { 'ApproximateReceiveCount': '1'},
u'Body': 'send message',
u'MD5OfBody': 'da29a3de5fd13de436de4f0dd4165206',
u'MessageId': 'd31731b4-87dc-4298-bb18-a6e7c59f8603',
u'ReceiptHandle': 'AQEB0kwuk19uzI/VKp0zwAd48sk8JR5hsa3p29XyogMJmGiuZpCudlLeDyu4lQlOZP5PGlxDkesmQNOCvWgF7qRFLCKSCtJQ+CVP/7IQCkhuv/LHVhgk1mJXec9B6NAsi7JderMFBAxkr9LrorGgCzRhduGNEdyznPXVHoZnuJwpHvhrr7IvCWefz8IDvyeDCiLl9BeHc6jyESMjxlG28SRNDJkY8TdCifU104h/26KzkfGZpkoaqOUMLZZnSQliWAEfdTcV6w+b2oDs9GTPuB2vxq1UXc5Qzh19RhKT51i1HS84ziP7N8sAyPsYRDnagKBwO84JX87u03sQg2bZDMWqjNSR+M1npc9Ln7Q737wNhTaaS24M78WaQCUiWpzYgHyM'}],
'ResponseMetadata': { 'HTTPStatusCode': 200,
'RequestId': '83ba4de2-8607-514f-82e8-02a658785ac1'}}

このようにメッセージを受け取る事が出来ます。 あるWorkerがQueueからメッセージを受け取ると、 可視性タイムアウト(Visible Timeout) の設定時間だけ、同一メッセージをQueueから取得することができなくなります。 この機能を使って、一定時間内、他のWorkerに同じメッセージが渡らない様にします。

sqs_dlq_07

通常は処理を終えたら、WorkerがQueueにあるメッセージを削除し、別のメッセージを受け取り、作業します。

Dead Letter Queue への移動タイミング

demoキューから取得したメッセージが削除されず、 可視性タイムアウト(Visible Timeout) が発生すると、demoキューに戻ります。

sqs_dlq_08

そして、もう一度receive_messageでメッセージを取得しようとすると、利用可能なメッセージにメッセージがあるにもかかわらずメッセージを取得することが出来ませんでした。

python run.py recv https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo
{ 'ResponseMetadata': { 'HTTPStatusCode': 200,
'RequestId': '7af78b35-8781-5ccf-9ce4-c4715853d01e'}}

マネージコンソールで、Queueを確認して見ると、 Dead Letter Queue に設定したdemo_dlqにメッセージが移動しています。 この事により、キューを取得しようとした際に、 ApproximateReceiveCount最大受信数(Maximum Receives) の値以上だったら、そのままメッセージを返さず、 Dead Letter Queue に設定したQueueに移動することがわかります。

sqs_dlq_09

Dead Letter Queue に移動したメッセージの確認

python run.py recv https://sqs.ap-northeast-1.amazonaws.com/123456789012/demo_dlq
{ u'Messages': [ { u'Attributes': { 'ApproximateReceiveCount': '2'},
u'Body': 'send message',
u'MD5OfBody': 'da29a3de5fd13de436de4f0dd4165206',
u'MessageId': 'd31731b4-87dc-4298-bb18-a6e7c59f8603',
u'ReceiptHandle': 'AQEBTECAZJ6pg2m1h1+fhRa0vUfc/8Ut2IRxuVxkUexSKnP95opRLk1STwcPbB04jh/KT3BNNu6MRqVJkZH1mMLqF7ef8RCDZuGcW//VEUAejcydm12q2u2lXz2m0CDuJE6SlPQjju6q419ewi4c5fp0o0EP8iEGI6wHwbhvdi1KhB7bQwnIfuEUw4FCyBfxbt0yB03HNSy1cLmkUssZucjhftutT/bcc/tucjOqzUZ3FxE+C25DWQ1kEzJdip4COrrYjfMdUXmvf7QKKnF6pc2V3eqc+2YkyoVB+kga0IxuZyg6yMhKqi7EIQRzU2M4kTd1z860TkG2j77e4vr5gDDbHBRP6V6g0GkIw0Dnonk4nzCzpdEvZ5k/XQD9xamtvj/JQBqU/bg3pH7h7DH40WegCg=='}],
'ResponseMetadata': { 'HTTPStatusCode': 200,
'RequestId': '178d7294-59f3-53b0-847d-3dd0b8de50de'}}

QueueUrlをdemo_dlqキュー側にしてreceive_messageをすると、先ほど取得したメッセージのMessageIdが一緒なので、同一メッセージだということがわかります。また、ApproximateReceiveCountReceiptHandle 以外値が同じのメッセージということも確認できます。

sqs_dlq_10

まとめ

以上のことから以下の事がわかりました。

  • 可視性タイムアウト(Visible Timeout) では、元のキューに戻るだけで Dead Letter Queue にメッセージが移動することは無かった
  • receive_messageを行った時に、Queueの Maximum Receives と メッセージの ApproximateReceiveCount の値によって、そのQueueのメッセージとして取得出来るか、メッセージが取得出来ずに Dead Letter Queue にメッセージが移動するか決まる