SNS ファンアウト
SNS トピックから複数の SQS キューや Lambda に同時配信し、1 つのイベントで複数の処理を並行実行するパターン
SNS ファンアウトとは
SNS ファンアウトは、SNS トピックに 1 つのメッセージを発行すると、サブスクライブしている複数の SQS キューや Lambda 関数にメッセージが同時配信されるパターンである。1 つのイベントをトリガーに、複数の独立した処理を並行実行できる。
[注文サービス] → SNS トピック → SQS (在庫引き当て) → Lambda A
→ SQS (メール通知) → Lambda B
→ SQS (分析データ) → Lambda C
→ Lambda D (直接呼び出し)
SNS → SQS の利点
SNS から Lambda を直接呼び出すこともできるが、SQS を挟むメリットがある。
| 構成 | メリット | デメリット |
|---|---|---|
| SNS → Lambda | シンプル、低レイテンシ | 配信リトライを使い切ると破棄される |
| SNS → SQS → Lambda | DLQ でエラー管理、バッチ処理、スロットリング | 若干のレイテンシ増加 |
SQS を挟むと、Lambda が失敗した場合にメッセージがキューへ戻り、可視性タイムアウトの経過後に再び受信される。受信回数が maxReceiveCount を超えたメッセージは SQS の DLQ (デッドレターキュー) へ移るので、後から原因を追える。
SNS から Lambda を直接呼ぶ構成にはこの受け皿が無い。SNS は配信リトライを使い切るとメッセージを破棄するため、購読に DLQ を設定していないと痕跡なく失われる。ここで言う DLQ は SNS の購読に付ける DLQ で、配信そのものが失敗したメッセージを受ける。SQS キューに付ける DLQ が受けるのは、届いた後の処理が繰り返し失敗したメッセージである。前半の失敗と後半の失敗は別の受け皿で拾うため、片方だけ用意すると残る方が盲点になる。SNS 購読の DLQ は通常の SQS キューで、redrive ポリシーの deadLetterTargetArn に ARN を書いて指定する。同一アカウント・同一リージョンのキューであることが条件で、FIFO トピックの購読には FIFO キューを使う。
配信形式と権限の落とし穴
SNS から SQS への配信は、既定ではペイロードが SNS の通知エンベロープに包まれる。キューから取り出した本文は Type や MessageId、TopicArn、Timestamp を含む JSON で、発行した内容はその Message フィールドに文字列として入っている。素の JSON が届くつもりで書いたコンシューマーは、この二重構造で解析に失敗する。購読属性の RawMessageDelivery を true にすると SNS のメタデータが外れ、発行した内容がそのまま届く。どちらの形式で受けるかを決めたら、そのキューを読む処理すべてで揃えておく。
権限はキュー側に書く。購読を作れていても、キューのポリシーで sns.amazonaws.com からの sqs:SendMessage を許可していなければメッセージは届かない。購読の状態は確認済みのまま送信だけが落ちるので、気付くのが遅れやすい失敗である。
{
"Sid": "AllowSnsTopic",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": "sqs:SendMessage",
"Resource": "<キューの ARN>",
"Condition": {
"ArnEquals": { "aws:SourceArn": "<トピックの ARN>" }
}
}
aws:SourceArn の条件は、第三者が自分のトピック経由でこのキューへ送り込むことを防ぐために置く。いわゆる混乱した代理問題への対策である。複数のトピックから受けるキューでは、トピックごとにステートメントを分けるか、aws:SourceAccount でアカウント単位に束ねる。
キューをサーバー側暗号化している場合、AWS マネージドキーのままでは SNS からのメッセージを受け取れない。カスタマー管理の KMS キーを作り、そのキーポリシーで SNS のサービスプリンシパルに暗号化操作を許可する。SNS 購読に付ける DLQ を暗号化する場合も同じ手当てが必要になる。
メッセージフィルタリング
フィルターポリシーは、トピックではなく購読に付ける JSON である。フィルターポリシーを持たない購読は、そのトピックに発行された全メッセージを受け取る。絞り込みは受け取る側の設定だと押さえておくと、配線を追いやすい。
発行側は、判定に使う値をメッセージ属性として付ける。
// メッセージ発行時に属性を付与
await sns.send(new PublishCommand({
TopicArn: ORDER_TOPIC_ARN,
Message: JSON.stringify(order),
MessageAttributes: {
orderType: { DataType: 'String', StringValue: 'premium' },
amount: { DataType: 'Number', StringValue: '50000' },
},
}));
購読側には、その属性に対する条件を置く。
{
"orderType": ["premium"],
"amount": [{ "numeric": [">=", 10000] }]
}
配列は「いずれかに一致」を意味し、キーを並べると全キー一致になる。数値比較を効かせるには発行側で DataType を Number にしておく必要があり、String のままでは文字列一致しか働かない。属性ベースの落とし穴は、発行側が属性を付け忘れたメッセージが条件を満たさず静かに配信対象外になることである。属性を増やしたくない場合は購読の FilterPolicyScope を MessageBody に切り替え、本文の JSON を直接条件にできる。この場合は本文が常に整形式の JSON である前提が要る。
EventBridge との使い分け
どちらも 1 つのイベントを複数の宛先へ届けるが、得意な形が違う。SNS は同じメッセージを多数の購読先へ配るのが速く、EventBridge は中身を見て宛先を選び分ける方が得意である。
| 観点 | SNS ファンアウト | EventBridge |
|---|---|---|
| フィルタリング | 購読フィルターポリシー (属性または本文) | イベントパターン (JSON パス) |
| 宛先の数 | 標準トピックで 1 トピックあたり 12,500,000 サブスクリプション (FIFO トピックは 100) | 1 イベントバスあたり既定 300 ルール、1 ルールあたり 5 ターゲット |
| スキーマ管理 | なし | Schema Registry |
| 適するケース | シンプルなファンアウト | 複雑なルーティング |
数値は 2026 年 8 月時点の既定クォータである。EventBridge のルール数は引き上げを申請できるが、1 ルールあたりのターゲット数 5 は引き上げできない。ルール数の既定が 300 ではなく 100 のリージョンもあるので、多数のルールを前提にするなら先に確認する。宛先を 6 つ以上にしたいときはルールを分けるか、ルールのターゲットに SNS トピックを置いてそこからファンアウトさせる。
購読数の上限が問題になることは実務ではまずない。先に当たるのは発行側で、標準トピックへの発行はリージョンとアカウント単位のクォータになる。東京リージョンの既定は毎秒 1,500 メッセージで、Publish と PublishBatch を合算した値である。ファンアウトは 1 回の発行で下流の処理数が倍になる構造なので、実際に詰まるのは下流の同時実行数や下流サービス側のクォータであることが多い。
設計時の判断は 2 つに絞られる。1 つは下流ごとに受け皿を持たせるかで、再処理や失敗調査が必要な処理は SQS を挟む。もう 1 つは絞り込みをどこでやるかで、購読フィルターで落とせるものは下流に届く前に落とす。届いてから捨てる設計は、下流の同時実行数と課金を無駄に使う。
この記事は役に立ちましたか?
関連用語
Pub/Sub
発行者と購読者が直接通信せず、メッセージブローカーを介して非同期にメッセージを交換するパターン
SNS と SQS
AWS のメッセージングサービスで、SNS がパブリッシュ/サブスクライブ、SQS がメッセージキューを提供する
イベント駆動アーキテクチャ
イベントの発行と購読を中心にシステムを構成し、サービス間の疎結合と非同期処理を実現するアーキテクチャスタイル
ファンアウト
1 つのイベントを複数のコンシューマーに同時配信するメッセージングパターン
メッセージキュー
プロデューサーとコンシューマーを非同期に接続し、メッセージを一時的に保持する通信基盤
SQS FIFO キュー
メッセージの順序保証と厳密な 1 回配信を提供する Amazon SQS のキュータイプ
関連する記事
エラーメッセージを読めるエンジニアは何が違うのか
エラーが出たときに冷静に原因を特定できる人と、パニックになる人。その差は経験だけでなく、読んできた本の種類にあります。デバッグ力を支える読書の傾向を分析します。
技術書の読書ログを GitHub で管理する - エンジニアらしい記録法
技術書の読書記録を GitHub リポジトリで管理する方法を紹介します。Markdown で読書ノートを書き、コミット履歴で読書の軌跡を残す、エンジニアならではの読書ログ術です。
技術書で学んだことを LT で発表する - 5 分で伝えるコツ
技術書の内容を 5 分の LT にまとめる構成テンプレートと、準備のコツ、発表が学習を加速させる理由を紹介します。