Amazon SQSを使って得た知見をまとめておく
Amazon SQS(Simple Query Service)を使う機会があったので、その過程で得た知見をざっとまとめておく。
キューURL
キューを作成するとURLが発行される。
SQSのAPIを使用するときは、このURLをエンドポイントとして、操作対象のキューを指定できる。
なお、このURLは以下のような規則で命名される。
https://sqs.{リージョン}.amazonaws.com/{AWSアカウントID}/{キューの名前}
メッセージの送信
キューへメッセージを送信するアクションには SendMessage
と SendMessageBatch
があり、一度に複数のメッセージを送信したい場合は SendMessageBatch
を使う。
送信可能なメッセージ件数は最大10件まで。
メッセージ件数分の SendMessageBatchRequestEntry
をリクエストパラメータとして含める。
SendMessageBatchRequestEntry
にはIdが必要であり、これは 一回のリクエストで送信される メッセージの中でユニークであれば良い。
このIdは SendMessageBatch
の結果として返ってくるメッセージを識別するために利用する。
注意しなければいけないのは、 バッチ内の個々のメッセージが失敗していても、バッチリクエストとしては成功として結果を返すことがある こと。
以下は、AWS SDK for PHPを使用していた場合の戻り値。
<?php // ... [ 'Failed' => [ [ 'Code' => '<string>', 'Id' => '<string>', 'Message' => '<string>', 'SenderFault' => true || false, ], // ... ], 'Successful' => [ [ 'Id' => '<string>', 'MD5OfMessageAttributes' => '<string>', 'MD5OfMessageBody' => '<string>', 'MessageId' => '<string>', 'SequenceNumber' => '<string>', ], // ... ], ]
このように、失敗したメッセージは Failed
へ、成功したメッセージは Successful
へ格納されてレスポンスが返ってくる。
そのため、必ず失敗したメッセージがないかを確認し(どのメッセージが失敗したかの判別には SendMessageBatchRequestEntry
のIdを使うと良いだろう)、適宜リカバリ処理を行う。
送信できるメッセージは最大で256KBと、大容量のデータ送信には不向きであるため、IDであったりリソースへのアクセスパスだったりをメッセージとして含めるのが良い。
メッセージの受信
キューからメッセージを受信するには ReceiveMessage
アクションを使う。
このアクションで最大10件までのメッセージを受信することができる。
メッセージの受信にはいくつかの注意点がある。
順番が保証されない
できるだけ古いメッセージから受信するようになっているが、あくまでベストエフォートであり、その順番が完全に保証されているわけではない。
当然、受信するメッセージの順番は指定することができない。
順番が保証されている前提でメッセージ受信後の処理を実装するのは避けること。
全てのメッセージを受信できることを保証しない
例えば、キューの中にメッセージがあるにもかかわらず、受信メッセージが空の場合もあり得るため、受信メッセージが空であるからキューの中身が空という保証はない。
このように、キューの中にあるメッセージを必ず受信できるとは限らない。
メッセージが重複する可能性がある
メッセージ受信アクションが複数のコンシューマによって同時に実行された場合、タイミングの問題で同じメッセージを受信してしまうことがある。
また、意図せず複数回メッセージ受信が実行され、メッセージが重複してしまうことも考慮しなければならない。
同じメッセージを複数回受信しても問題ないように実装する。
できるだけ同じメッセージを受信しないように、受信メッセージを処理したら削除してしまうのが基本となる。
ただ、メッセージ受信→メッセージ受信後の処理→メッセージ削除という流れの間に、他のコンシューマが同じメッセージを受信してしまう可能性がある。
これを防ぐために Visibility Timeout
を設定して、設定した時間だけ他のコンシューマからの同メッセージ受信を不可能にすると良い。
設定する時間は、受信から削除までの一連の処理時間よりも多めの時間を設定する。
なお、この辺りの問題を解消した「FIFOキュー」も用意されている。
ただし、標準キューとは違い、FIFOキューにはスループットに制限があるため、要件に合わせてFIFOキューを利用するかしないかを決めると良い。
ロングポーリングの設定
Receive Message Wait time
を設定することでロングポーリングを有効にできる。
ロングポーリングを有効にすると、キューにメッセージが存在しない場合は接続した状態で受信処理を待機し、キューにメッセージが入ると即時受信するようになる。
これにより、キューが空のときのリクエスト数を削減できる。
メッセージの削除
メッセージを取得して無事処理が終わったら、一般的にはそのメッセージは削除することになる。
このメッセージ削除に必要になる識別子が ReceiptHandle
である。
ReceiptHandleを指定することで削除対象メッセージを特定できるが、ReceiptHandleは同一メッセージでも取得するたびに変化する。
そのため、ReceiptHandleは常に最新のものを使用しなければならない。
エラーハンドリング
SQSを利用するときに限らず、キューにアクセスできなかった、メッセージ制限サイズを超えてしまった、などのエラーを想定して処理を組む必要がある。
また、何かしらの原因で処理が正常に行われずキューにメッセージが残り続けてしまう問題を解決しなければいけない。
このための機能としてデッドレターキューが用意されている。
指定回数以上のメッセージ受信が行われた場合、そのメッセージをデッドレターキューに移動する。
これでキューにメッセージが残り続けてしまう問題を解消することができ、正しく処理されなかったメッセージを分離することで、原因の調査に利用することもできる。
デッドレターキュー監視用のコンシューマを用意しておき、デッドレターキューからメッセージを受信したら通知を飛ばすようにするとメッセージが処理できない状況をすぐに把握できる。
また、デッドレターキューも通常のキューと同様メッセージ保持期間が存在するので、必要なら永続化しておく。
なお、複数のキューに同じデッドレターキューを紐付けることもできる。
開発環境について
開発環境からSQSの接続が制限される場合、「ElasticMQ」のようなツールを使うと良い。
ただ、ElasticMQにはSQSのようにGUIがない(ElasticMQのためにGUIを提供しているOSSもあるが)ので、例えばキューにあるメッセージ件数を確認したい場合は GetQueueAttributes
アクションを使い ApproximateNumberOfMessages
などで確認できる。