対象読者: AWSをこれから触る人。 AWS勉強シリーズの1本目で、Amazon SQSがどういうものかを勉強します。全体の地図は索引記事にあります。
SQSとは何か
Amazon SQS(Simple Queue Service)は、**処理の待ち行列(キュー)**を置くためのサービスです。典型的には「Webサーバーが注文を受け付ける→メール送信や画像変換などの重い処理は後ろの係がやる」という構成で、受付と係の間にSQSを挟みます。
間に待ち行列があると、係のプログラムが止まっていても仕事は失われずキューに溜まりますし、注文が急増しても係は自分のペースで処理できます。サービス同士を直接繋がないための緩衝材です。
| 用語 | 意味 |
|---|---|
| キュー | メッセージの待ち行列本体 |
| メッセージ | キューに入れる仕事1件分の中身 |
| ワーカー | キューから仕事を取り出して処理する側のプログラム |
| 可視性タイムアウト | 受信したワーカーがそのメッセージを独占できる制限時間 |
| ReceiptHandle | 受信ごとに発行される受領証。削除に使う |
| DLQ(デッドレターキュー) | 何回やっても処理に失敗するメッセージの隔離先 |
一番大事な仕組み。受信してもメッセージは消えない
SQSで最初につまずくのがここです。メッセージは受信しただけでは消えません。 受信すると一定時間(可視性タイムアウト、既定30秒)だけ「不可視」になり、他のワーカーから見えなくなります。時間内に削除されなければ、メッセージはキューに復活して、また誰かが受信できるようになります。
なぜこうなっているかというと、処理の途中でワーカーが落ちても仕事を失わないためです。受信した瞬間に消える設計だと、処理する前にワーカーが死んだらその仕事は永遠に消えます。SQSは「削除の連絡が来るまで完了と見なさない」ことで、これを防いでいます。
だから使う側のルールはこうなります。
- 受信= 仕事の予約(独占権をもらう)
- 削除= 処理完了の報告
- 削除を忘れると、成功した処理が可視性タイムアウトのたびに再配達されて二重に走る
使い方
基本は「キューを作る→送る→受け取る→削除する」の4手です。Python(boto3)での最小の形はこうなります。
import boto3
sqs = boto3.client("sqs", region_name="ap-northeast-1")
# ① キューを作る(URLがそのキューの宛先になる)
q = sqs.create_queue(QueueName="my-queue")["QueueUrl"]
# ② 送る
sqs.send_message(QueueUrl=q, MessageBody="やってほしい仕事の中身")
# ③ 受け取る
msg = sqs.receive_message(QueueUrl=q)["Messages"][0]
print(msg["Body"]) # -> やってほしい仕事の中身
# ④ 処理が終わったら削除する(これをやらないと同じ仕事がまた来る)
sqs.delete_message(QueueUrl=q, ReceiptHandle=msg["ReceiptHandle"])
失敗し続けるメッセージはDLQへ隔離する
中身が壊れていて何回処理しても失敗するメッセージがキューに居座ると、復活→失敗→復活を繰り返します。対策がDLQ(デッドレターキュー)で、「指定回数受信されても削除されなかったメッセージを、別のキューへ自動で移す」設定です。
# 退避先キューを作って、本体キューにRedrivePolicyを設定する
dlq = sqs.create_queue(QueueName="my-queue-dlq")["QueueUrl"]
dlq_arn = sqs.get_queue_attributes(
QueueUrl=dlq, AttributeNames=["QueueArn"])["Attributes"]["QueueArn"]
q = sqs.create_queue(
QueueName="my-queue",
Attributes={
"RedrivePolicy": '{"deadLetterTargetArn":"%s","maxReceiveCount":"3"}' % dlq_arn
})["QueueUrl"]
maxReceiveCountは「何回までの受信を許すか」です。3にすると、3回受信されても削除されなかったメッセージが、次の受信のタイミングでDLQへ移ります。本番でDLQ無しの運用は、壊れたメッセージの行き場が無くなるので避けてください。
標準キューとFIFOキュー
キューには2種類あります。
| 標準キュー | FIFOキュー(名前が .fifo で終わる) |
|
|---|---|---|
| 順序 | 保証されない | 送った順に届く(同じメッセージグループ内で保証) |
| 重複 | まれに同じメッセージが2回届く | 同じ内容の重複送信を自動で1件に畳める |
| 性能 | ほぼ無制限 | スループット上限あり |
| 使い分け | 量を捌く。受信側を「2回走っても大丈夫」に作る | 順序と一意性が大事な処理(決済など) |
無料で試す方法
本物のAWSアカウントが無くても、motoというローカルのそっくりさんでSQSのAPIはひと通り動かせます。
python3 -m venv venv
./venv/bin/pip install boto3 moto
スクリプトの先頭に2行足すだけで、上のコードがそのままPCの中で動きます。
from moto import mock_aws
@mock_aws # この中のboto3呼び出しは全部ローカルの偽AWSに行く
def main() -> None:
...
motoはAPIの動きを再現するもので、本物の分散システムとしての性質(標準キューのまれな重複など)までは再現しない点だけ頭に置いてください。
まとめ
- SQSはサービス間の待ち行列。受付と処理を切り離す緩衝材
- 受信では消えない。削除が処理完了の報告で、忘れると二重実行になる
- 本番はDLQを設定して、失敗し続けるメッセージの行き場を作る
次回: SNS入門。1回のpublishが購読者全員に届く仕組みと、SQSとの違い
参考
- Amazon SQS 公式ドキュメント
- シリーズ索引: AWSとは何か