Amazon SQSによる非同期メッセージング

Amazon SQSでプロデューサーとコンシューマーを分離し、キュータイプ、配信制御、再試行、スケーリングを設計する方法を学びます。

学習順Step 105 / 121サービスSQS試験ドメイン開発

Amazon SQSによる非同期メッセージング

Amazon Simple Queue Service(Amazon SQS)は、アプリケーション間でメッセージを一時的に受け渡す、フルマネージド型のキューイングサービスです。2004年にベータ版が発表され、2006年に一般公開された初期のAWSサービスの1つです。メッセージを送る側をプロデューサー、メッセージを取得して処理する側をコンシューマーと呼びます。両者の間にキューを置くと、送信側は受信側の稼働状況や処理速度を待たずに処理要求を登録できます。

密結合した構成を非同期に分離する

モノリシックなアプリケーションでは、複数の機能がひとまとまりでデプロイされ、互いに直接呼び出し合うことがあります。ある処理だけが急増しても一体のアプリケーション全体を拡張する必要があり、機能間の依存によって障害が広がることもあります。

マイクロサービスでは、機能を独立したサービスとして開発、デプロイ、スケールできます。ただし、サービス同士がすべて同期呼び出しで連鎖すると、下流サービスの遅延や停止が呼び出し元へ伝わります。SQSを間に置いて処理要求をメッセージとして渡せば、送信側と処理側は互いの稼働タイミングに依存せずに動けます。

たとえば、画像投稿を受け付けるサービスは、画像の変換処理をキューへ登録した時点で利用者に受付結果を返せます。画像変換ワーカーは、自身の処理能力に合わせてキューから仕事を取り出します。変換要求が一時的に増えた場合、キューがその差分を保持し、ワーカーを増やすまでのバッファーになります。

SQSを介して投稿受付サービスと画像変換ワーカーを非同期に接続する構成

この方式では、送信側と処理側を独立してスケールでき、処理の一時停止中もメッセージを保持できます。一方で、処理結果がすぐに必要な場合には同期APIの方が適しています。また、メッセージの重複や遅延を前提に、処理を冪等にする、失敗を記録する、キューの滞留を監視するといった設計が必要です。

メッセージの状態と識別子

メッセージは、プロデューサーが送信してからコンシューマーが受信するまでキューに保存されます。受信されたメッセージも、処理が終わって削除されるまではキュー内にあります。受信直後は**処理中(in flight)**として他のコンシューマーから見えなくなりますが、メッセージそのものが削除されたわけではありません。

受信に成功したら、処理を完了してからメッセージを削除します。削除しないまま可視性タイムアウトが過ぎると、同じメッセージが再び受信可能になり、別のコンシューマーが処理することがあります。SQSでは「受信したから処理済み」とは限らないため、処理後の削除と重複への備えが重要です。

識別子 意味と用途
キューURL ReceiveMessageやSendMessageなど、キューを操作するときに指定するURL
キューARN IAMポリシーやリソースポリシーでキューを指定するときに使うARN
メッセージID メッセージ送信後にSQSが割り当てる識別子。追跡やログ記録に使う
Receipt handle メッセージを受信するたびに返される受信ハンドル。削除や可視性変更で使う

DeleteMessageやChangeMessageVisibilityではメッセージIDではなく最新のReceipt handleを指定します。同じメッセージを再受信するとハンドルも変わるため、古いハンドルでは意図した操作にならないことがあります。

標準キューとFIFOキュー

SQSには標準キューとFIFOキューがあります。順序保証が必要か、処理量を優先するかを基準に選択します。保存メッセージ数に固定の上限はありませんが、キューには保持期間や処理中メッセージ数などのクォータがあります。

特性 標準キュー FIFOキュー
順序 ベストエフォート。送信順と異なる順序で届くことがある 同じメッセージグループ内の順序を維持
重複 少なくとも1回の配信。重複配信が起こり得る 5分間の重複排除期間があり、送信重複を抑制
並列性 高いスループットで並列処理できる 異なるメッセージグループを並行処理できる
キュー名 通常のキュー名 キュー名の末尾に.fifoが必要
選択の目安 重複時に安全に再実行でき、順序を問わないタスク 同一対象のイベントを順番に処理する必要がある場合

標準キューは、メッセージが少なくとも1回配信されることを前提にします。まれな重複や順序の入れ替わりが許容できるワークロードに適します。たとえば、独立した帳票生成ジョブや、個別に処理できる通知生成などです。

FIFOキューでは、すべてのメッセージにMessageGroupIdを指定します。同じグループのメッセージは順番に処理され、そのグループの先行メッセージが処理中である間、後続メッセージはその順序を追い越しません。異なるグループは並行処理できるため、たとえば倉庫の棚ごとにグループを分ければ、同じ棚の在庫更新順を保ちながら別の棚の更新を同時に進められます。

MessageDeduplicationIdは、FIFOキューにおける送信側の重複排除に使うトークンです。同じIDのメッセージは5分間の重複排除期間中、重複として扱われます。コンテンツベースの重複排除を有効にすると、本文からIDが生成されます。この仕組みはアプリケーション全体での「一度だけの副作用」を保証するものではありません。受信後に処理が再実行される可能性を考慮し、必要に応じて処理側でも冪等性を確保します。

通常のFIFOスループット上限はAPI操作あたり毎秒300トランザクション(バッチ利用時は毎秒3,000メッセージ)が目安です。高スループットFIFOを有効にすると、リージョンごとのクォータまで引き上げられます。上限はリージョン、設定、メッセージグループ数で異なるため、具体的な容量設計ではメッセージクォータを確認してください。

メッセージサイズと保持期間

現在、SQSのメッセージ本文は最大1 MiBです。より大きなペイロードを扱うときは、Amazon S3にデータ本体を保存し、その参照情報をSQSメッセージに含める拡張クライアント方式を利用できます。この方式ではS3側の保存やリクエストにも料金が発生します。

受信APIでは、1回のReceiveMessageで最大10件を取得できます。メッセージ属性は1件あたり最大10個です。メッセージ保持期間はデフォルト4日、設定範囲は60秒から14日です。明示的に削除されないメッセージは保持期間が過ぎると失効します。処理が完了したメッセージは、保持期限を待たずにDeleteMessageで削除してください。

ポーリング方式:ショートとロング

SQSでは、コンシューマーがReceiveMessageを呼び出してメッセージを取得します。これはポーリングと呼ばれます。

  • ショートポーリングでは、SQSは利用可能なメッセージを確認してすぐ応答します。キューが空であれば空の応答になることがあり、メッセージが届いていてもその応答に含まれないことがあります。空振りが多いとAPIリクエスト数と費用が増えます。
  • ロングポーリングでは、メッセージがすぐ見つからない場合も指定時間だけ待ってから応答します。WaitTimeSecondsは0秒から20秒まで設定でき、0秒はショートポーリングです。空の応答や空振りを減らしやすいため、多くのワークロードで有効です。

受信待機を設定するには、キュー属性ReceiveMessageWaitTimeSecondsで既定値を指定する方法と、受信リクエストごとにWaitTimeSecondsを指定する方法があります。HTTPクライアントのタイムアウトは、ロングポーリングの待機時間より長くしてください。

可視性タイムアウト:受信後の処理時間を確保する

メッセージを受信すると、SQSは一定期間ほかのコンシューマーから隠します。この期間が可視性タイムアウトです。デフォルトは30秒で、0秒から12時間まで設定できます。

  1. コンシューマーがメッセージを受信する。
  2. メッセージは可視性タイムアウト中、ほかのコンシューマーには返されない。
  3. 処理が成功したら、コンシューマーがReceipt handleを使ってメッセージを削除する。
  4. 処理が終わらずタイムアウトが満了すると、メッセージが再び受信可能になる。

タイムアウトを実際の処理時間より短くすると、処理中のメッセージが別のワーカーにも配信され、重複実行につながります。一方、長すぎる設定では、処理に失敗したメッセージが再試行されるまで待たされます。処理時間のばらつきが大きい場合は、ChangeMessageVisibilityで処理中に可視性タイムアウトを延長できます。ただし、可視性タイムアウトは排他ロックや永続的なリースではなく、重複配信を完全になくす仕組みではありません。

LambdaのイベントソースとしてSQSを使うときは、Lambda関数のタイムアウトに対してキューの可視性タイムアウトを十分長くします。AWSは関数タイムアウトの少なくとも6倍を設定することを推奨しています。バッチ再試行に必要な余裕も考慮してください。

遅延キューとメッセージタイマー:初回配信を遅らせる

遅延キューは、送信された新しいメッセージがコンシューマーに見えるまでの時間を遅らせます。キュー単位で0秒から15分まで設定できます。個別のメッセージだけ遅延させたい場合は、標準キューのメッセージタイマーを使います。個別のタイマーはキューの既定値より優先されます。FIFOキューは個別メッセージタイマーをサポートしません。

機能 メッセージが見えなくなる時点 主な用途
遅延キュー / メッセージタイマー キューへ送信した直後 初回処理の開始を遅らせる
可視性タイムアウト コンシューマーが受信した後 処理中の再配信を一定時間抑える
ロングポーリング 受信リクエスト中にメッセージがないとき 空の応答を減らしながら到着を待つ

遅延キューは初回配信を、可視性タイムアウトは受信後の再配信を遅らせる

この2つはどちらもメッセージを一時的に不可視にしますが、適用されるタイミングが異なります。送信直後から見せたくない場合は遅延キュー、処理開始後にほかのワーカーへ再配信したくない場合は可視性タイムアウトです。15分を超える将来の時刻に処理を予約する用途では、EventBridge Schedulerなどのスケジューリング機能を検討します。

遅延キューの設定変更が既存メッセージにどう反映されるかもキュー種別で異なります。標準キューでは変更は既存メッセージに遡及せず、FIFOキューでは既存メッセージにも反映されます。

デッドレターキュー:繰り返し失敗するメッセージを分離する

デッドレターキュー(DLQ)は、ソースキューで正常に処理できないメッセージを隔離するための別キューです。ソースキューのredrive policyでDLQとmaxReceiveCountを指定します。メッセージの受信回数が設定値を超えると、SQSはそのメッセージをDLQへ移します。受信試行回数を小さくしすぎると、一時的な依存先障害でもすぐDLQへ移るため、再試行に必要な回数を設定します。

DLQはソースキューと同じAWSアカウントおよびリージョンに作成し、標準キューには標準DLQ、FIFOキューにはFIFO DLQを対応させます。redrive allow policyを使うと、そのDLQを利用できるソースキューを制限できます。DLQのメッセージ数にCloudWatchアラームを設定し、内容や処理ログを調査した後、redriveでソースキューなどへ戻す運用ができます。

標準キューでは、DLQへ移動してもメッセージの最初の送信時刻が保持されます。そのため、元キューでの滞留時間もDLQの保持期間に含まれます。DLQの保持期間はソースキューより長く設定するのが一般的です。FIFOキューではDLQへ移動した際に送信時刻がリセットされます。また、順序に依存する業務でメッセージをDLQへ飛ばすと、後続処理との整合性や順序を崩すことがあるため、DLQの利用自体が適切か検討します。

優先度、リクエスト/レスポンス、一時キュー

SQSには標準の優先度付きキューという型はありません。処理優先度を表現したい場合、優先度別に複数キューを用意し、コンシューマー側が優先度の高いキューから確認する方法があります。この方式では、低優先度キューをいつ処理するか、各キュー間でワーカーをどう配分するかも設計します。

SQSは非同期連携に使いますが、リクエスト/レスポンス型のやり取りも組み立てられます。要求に返信先キューの情報と相関IDを含め、バックエンドが応答を返信先キューへ送ります。要求元はそのキューから応答を受け取ります。一時キュークライアントは、返信を受け取った後に一時的なキューを削除する処理を支援します。短命の通信先を多数扱うパターンには、仮想キュー機能を利用できます。仮想キューはクライアント側の受信先を共有キュー上に多重化するため、個々の仮想キュー作成のためのSQS API呼び出しは発生しません。実キューとメッセージの送受信には通常どおり料金がかかります。

バッチ処理と代表的なAPI

バッチAPIは、1リクエストで最大10件のメッセージをまとめて扱います。API呼び出し数を抑えられますが、バッチの一部だけが失敗する場合があるため、応答内の成功項目と失敗項目を個別に確認します。

API 用途
CreateQueue / DeleteQueue キューを作成 / 削除する
GetQueueUrl / GetQueueAttributes / SetQueueAttributes キューURLや属性を参照 / 設定する
SendMessage / SendMessageBatch メッセージを1件 / 複数件送信する
ReceiveMessage メッセージを最大10件取得する
DeleteMessage / DeleteMessageBatch 処理済みメッセージを1件 / 複数件削除する
ChangeMessageVisibility / ChangeMessageVisibilityBatch 受信済みメッセージの可視性タイムアウトを変更する
PurgeQueue キュー内のメッセージを消去する

料金はAPIリクエスト数だけでなく、リクエストのメッセージ量、キュー種別、暗号化、データ転送などに関係します。1回のリクエストで最大10件を処理でき、ペイロードの64 KB単位でリクエスト料金が計上されます。ロングポーリングやバッチ処理は不要なAPI呼び出しを抑えるのに役立ちます。具体的な金額はリージョンや利用量で変わるため、Amazon SQS料金を確認してください。

IAM、キューポリシー、暗号化

SQSのアクセス制御には、利用者やサービスのIAMアイデンティティポリシーと、キューに設定するリソースベースのキューポリシーがあります。キューポリシーでは、別アカウントのプロデューサーにSendMessageだけを許可するなど、キュー側でアクセス主体と操作を制御できます。クロスアカウントアクセスでは、送信元のIAM許可とキューのリソースポリシーの両方を確認します。

新しく作成するキューでは、保存中のメッセージに対するサーバー側暗号化(SSE-SQS)がデフォルトで有効です。SSE-KMSを選び、SQS管理のKMSキーまたはカスタマー管理KMSキーを使うこともできます。KMSキーを使う場合、プロデューサーとコンシューマー、連携するAWSサービスに必要なKMS権限とキーポリシーを設定します。SQSキューのポリシーで許可されていても、KMSキー側で拒否されれば暗号化キューを利用できません。カスタマー管理キーでは、SQSからKMSへの呼び出し料金も考慮します。

LambdaやAuto Scalingとの連携

Lambdaイベントソースマッピング

SQSからLambdaを起動する場合、Lambdaのイベントソースマッピングがキューをポーリングし、メッセージをバッチにまとめて関数へ渡します。関数が正常に処理すると、イベントソースマッピングが処理済みメッセージを削除します。失敗時は、バッチ再試行や部分バッチ失敗の設定、可視性タイムアウト、DLQまたはオンフォルト先の設計が関係します。

Lambdaのイベントソースマッピングは、キューにメッセージが蓄積すると処理を並列化します。標準キューでは複数バッチを並行処理でき、FIFOキューでは同一メッセージグループの順序を保ちながら処理します。処理量、関数の同時実行上限、バッチサイズ、メッセージグループ数が実際の処理速度に影響します。

CloudWatchとワーカーのスケーリング

SQSはキューの状態をCloudWatchメトリクスとして公開します。たとえば、ApproximateNumberOfMessagesVisibleはすぐに受信できるメッセージ数、ApproximateNumberOfMessagesNotVisibleは処理中のメッセージ数、ApproximateNumberOfMessagesDelayedは遅延中のメッセージ数の概算です。空の受信が増えている場合はNumberOfEmptyReceivesも調査材料になります。これらの値は近似値であり、すべての属性が常に厳密な即時値とは限りません。

EC2 Auto ScalingグループやECSサービスのワーカー数は、キューの滞留量に応じて調整できます。キューの可視メッセージ数だけを基準にすると、既に起動しているワーカー数を考慮できません。ApproximateNumberOfMessagesVisibleを稼働中のインスタンス数やタスク数で割ったワーカーあたりのバックログを使うと、許容処理待ち時間に沿ってスケール目標を設計できます。滞留量が閾値を超えたときのアラームや通知を設ければ、処理能力不足やコンシューマー障害を検知できます。

SNS、SQS、Kinesisを使い分ける

サービス 配信モデル 向いている用途
Amazon SQS コンシューマーがポーリングするキュー。通常は1つの処理系へ仕事を分配する 個別のタスクやコマンドを蓄積し、ワーカーへ分配する
Amazon SNS Pub/Sub型のプッシュ配信。1つの発行を複数の購読先へ配信する 同じイベントを複数の独立したシステムへ通知する
Amazon Kinesis Data Streams 保持されたストリームをコンシューマーが読み進める。順序はシャード単位 イベント列を継続的に取り込み、複数のコンシューマーでリアルタイム処理・分析する。レコードはデフォルト最大1 MiBで、ストリーム設定により最大10 MiBまで拡張できる

複数の独立した処理先へ同じイベントを届けたい場合、SNSから複数のSQSキューへファンアウトし、各処理先が自分のキューを消費する構成がよく使われます。Kinesisはデータストリームの取り込みや順序付き処理、再読込などに適し、SQSはワークアイテムの受け渡しと処理待ちのバッファーに適します。メールの送受信機能はAmazon SESの役割です。

設計時に押さえること

  • 順序が不要で高い並列性を重視するなら標準キュー、グループ内の順序が必要ならFIFOキューを選ぶ。
  • 受信後に処理が成功したと確認してから削除し、再配信に備えて処理を冪等にする。
  • 処理時間に合った可視性タイムアウトを設定し、長時間処理では必要に応じて延長する。
  • 空の受信やリクエスト料金を抑えるためにロングポーリングとバッチAPIを検討する。
  • 再試行回数を超えたメッセージの調査・再投入手順を含めてDLQを設計する。
  • CloudWatchでキューの滞留、処理中件数、空の受信、DLQの増加を監視する。
  • 15分を超える時刻指定や定期実行にはSQSの遅延機能ではなくスケジューラーを使う。

公式ドキュメント

重要ポイント

  • ▸SQSはプロデューサーとコンシューマーを非同期に分離し、需要の変動をキューで吸収する
  • ▸標準キューは高スループット向け、FIFOキューはメッセージグループごとの順序制御向け
  • ▸ロングポーリング、可視性タイムアウト、遅延配信はそれぞれ異なるタイミングを制御する
  • ▸デッドレターキューは処理に繰り返し失敗したメッセージを隔離し、調査や再処理に使う
  • ▸LambdaやAuto Scalingはキューの滞留量に応じて処理能力を調整できる
  • ▸SQSはタスク配信向けであり、優先度キューやストリーム分析には別の設計・サービスが必要

このトピックの学習を完了しますか?

完了状態はいつでも切り替えられます