AWS / SAP-C02

SAP-C02-075 · セクション 09 / タスク 2.4

Loose CouplingとApplication Integration

Amazon SQS / Amazon SNS / AWS Step Functions / Amazon EventBridge / Amazon MQ / 疎結合 / 非同期処理 / 依存関係の分離

ゴール

アプリケーション間に依存関係があるとき、接続関係を見るだけではなく、

サービスA
↓
サービスB
↓
サービスC

から、

Bが停止したとき
Aまで失敗するのか?

AとBは
同時に利用可能である必要があるのか?

Aが処理完了を待たず
安全に要求を引き渡せるか?

まで判断します。

AWS Well-Architected Frameworkでも、疎結合化によって障害の影響範囲をコンポーネント単位へ縮小することが推奨されています。

1. 同期呼び出しでは「相手が今動いていること」が依存条件になる

例えば注文サービスが在庫サービスを直接同期呼び出ししているとします。

利用者
  ↓
注文サービス
  ↓
在庫サービス

注文を成立させるために在庫サービスからの応答が必須なら、

在庫サービス停止
↓
注文サービスが待機
↓
タイムアウト
↓
注文受付も失敗

となります。

この構成では、下流サービスの可用性が、上流サービスの可用性条件に含まれています。

ただし、同期依存そのものが悪いわけではありません。

例えば、

その場で在庫有無を確定しなければ
利用者へ注文結果を返せない

という業務要件なら、下流の結果を待つこと自体が必要です。

したがって最初に、

下流処理の結果を
その場で必要とするか?

を確認します。

2. 疎結合化では「処理完了」と「安全な受け渡し」を分ける

業務上、下流処理をその場で完了させる必要がない場合は、

注文サービス
    ↓
信頼できる受け渡し先
    ↓
在庫サービス

という境界を作れます。

呼び出し元が確認するのは、

在庫処理が完了したか

ではなく、

後から処理できる状態で
要求を安全に引き渡せたか

です。

在庫サービス停止
↓
要求は中間層に残る
↓
注文受付は継続
↓
在庫サービス復旧
↓
未処理要求を処理

という障害分離が可能になります。

つまり、疎結合化の判断は、

同期処理
↓
何でも非同期化

ではなく、

その場で下流結果が必要か
↓
不要なら
安全な非同期境界を作れるか

です。

3. Amazon SQS:1つの作業を1つの論理処理系へ渡す

Amazon SQSは、送信側と受信側が同時に利用可能である必要をなくすためのメッセージキューとして利用できます。

作業を依頼する側
      ↓
 Amazon SQS
      ↓
コンシューマー群

決定打は、1件の作業を、1つの論理処理系が後から処理できればよいことです。

例えば、

注文受付
↓
Amazon SQS
↓
在庫更新ワーカー群

なら、在庫更新側が一時停止しても注文受付との稼働時間を分離できます。

Amazon SQS標準キューでは少なくとも1回の配信を前提とし、重複配信や順序の入れ替わりが発生する可能性があります。

4. SQSでは「受信」と「処理完了」を分ける

Amazon SQSからメッセージを受信しただけでは、処理完了にはなりません。

メッセージを受信すると、可視性タイムアウトの間は他のコンシューマーから見えなくなります。

メッセージ受信
↓
一時的に非表示
↓
処理成功
↓
DeleteMessage
↓
完了

一方、

メッセージ受信
↓
ワーカー障害
↓
DeleteMessageされない
↓
可視性タイムアウト終了
↓
再び受信可能

となります。

したがって、SQS標準キューのコンシューマーでは、同じメッセージを再処理しても結果が壊れないべき等性を考慮します。

繰り返し処理に失敗するメッセージは、デッドレターキューへ分離できます。

5. 順序が決定条件ならSQS FIFOを評価する

Amazon SQS標準キューでは、少なくとも1回の配信とベストエフォートの順序を前提にします。

一方、

同じ口座への更新
注文状態の遷移
同じ商品に対する順序依存処理

など、処理順序が業務結果を変える場合はSQS FIFOキューを評価します。

例えば、

口座A
1 → 2 → 3

口座B
1 → 2 → 3

としたい場合、

MessageGroupId = 口座ID

として、同一グループ内の順序を維持しながら異なるグループを並行処理できます。

したがって、

非同期にしたい
↓
SQS

だけで終わらず、

順序要件
重複要件

も確認します。

6. Amazon SNS:同じ出来事を複数の独立処理へ届ける

Amazon SNSは、発行・購読型のメッセージングを提供します。

              ┌→ 処理A
発行元 → SNS ─┼→ 処理B
              └→ 処理C

同じ出来事を複数の独立した処理系へ通知したい場合に適しています。

ただし信頼性設計では、

同じイベントを
3つの処理へ届けたい

だけでなく、

処理Bが停止しても
処理AとCは止めない

処理Bは復旧後に
停止中のイベントを処理したい

という要件も確認します。

この場合は、

                    ┌→ SQS A → 処理A
イベント発行 → SNS ┼→ SQS B → 処理B
                    └→ SQS C → 処理C

という構成を評価します。

SNSで同じメッセージをファンアウトし、各処理系に専用のAmazon SQSキューを持たせれば、各処理系が独立した未処理メッセージを保持できます。

7. 「複数コンシューマー」の意味を分ける

1つのキューを複数ワーカーで処理する

Amazon SQS
├→ ワーカー1
├→ ワーカー2
└→ ワーカー3

これは、同じ種類の作業を分担するワーカー群です。1件のメッセージを全ワーカーへそれぞれ届ける構成ではありません。

独立した複数処理へ同じイベントを配信する

Amazon SNS
├→ SQS A
├→ SQS B
└→ SQS C

こちらは、同じイベントを、それぞれ別の責任を持つ処理系が独立して処理する構成です。

したがって、

複数のコンシューマーが必要

という表現だけで判断せず、

同じ仕事を分担するのか

同じイベントを
それぞれ独立して処理するのか

を分けます。

8. Amazon EventBridge:イベントの内容から処理先を決める

Amazon EventBridgeでは、イベントバスに到着したイベントをルールと照合し、一致するターゲットへルーティングできます。

アプリケーションA ─┐
AWSサービス ──────┼→ EventBridge
アプリケーションB ─┘
                       │
               イベントパターン
                       │
             ┌─────────┼─────────┐
             ↓         ↓         ↓
         ターゲットA ターゲットB ターゲットC

決定打は、

イベント発行側は
受信側を直接知らなくてよい

イベントの内容から
処理先を選択したい

という要件です。

Amazon SNSにもサブスクリプションフィルタリングがあります。

したがって、

フィルタリングが必要
=
必ずEventBridge

とは判断しません。

EventBridgeの重要な特徴は、イベントバスとイベントパターンを介して、イベント発行側と処理先を分離できることです。

9. EventBridgeの再試行とSQSの未処理メッセージ保持を分ける

EventBridgeではターゲットへの配信に失敗すると再試行できます。既定では、再試行可能なエラーについて最大24時間、最大185回再試行します。

ただし、

処理系が最大7日間停止する可能性がある

停止中の対象イベントを失わず

復旧後にも
処理系自身の速度で全件処理したい

という要件を、EventBridgeの再試行だけで満たすことはできません。

この場合は、

EventBridge
↓
Amazon SQS
↓
処理系

のように、処理系専用の未処理メッセージ保持場所を設けます。

Amazon SQSのメッセージ保持期間は、

デフォルト:4日
最大:14日

です。

保持期間を決めるときは、最大停止時間だけでなく、

最大停止時間
+
復旧後に未処理分へ
追いつくために必要な時間

まで考慮します。

例えば最大7日間停止し、復旧後に1日程度かけて未処理分へ追いつく可能性があるなら、

7日停止
+
1日追いつき
↓
8日以上の保持期間

を設定します。

つまり、

最大7日停止
↓
保持期間を7日にする

だけでは境界上のメッセージを安全に処理できるとは限りません。

SQSの保持期間は最大14日なので、最大停止時間と追いつき時間の合計が14日を超える要件では、SQS単体で要件を満たせるとは判断しません。

10. AWS Step Functions:複数ステップの状態を管理する

AWS Step Functionsは、

処理A
↓
条件分岐
↓
処理B
↓
待機
↓
処理C

のように、複数ステップの処理順序、状態、分岐、待機をワークフローとして管理したい場合の候補です。

例えば、

注文
↓
在庫確保
↓
支払い
↓
承認待ち
↓
出荷

という一連の処理を、1つの状態付きワークフローとして管理できます。

Amazon SQSのような、

コンシューマーが
未処理作業を取り出す
汎用キュー

とは責任が異なります。

ここでの決定条件は、

複数の処理を
単に非同期で渡したい

ではなく、

複数ステップの
状態・順序・分岐・待機を
一連の処理として管理したい

ことです。

Standard ワークフローとExpress ワークフローには、実行時間や実行保証などの違いがありますが、ここでは詳細なタイプ選択ではなく、状態付きオーケストレーションが必要かどうかを判断します。

11. Amazon MQ:既存メッセージブローカーとの互換性が決定打になる

Amazon MQは、Apache ActiveMQ ClassicとRabbitMQ向けのマネージドメッセージブローカーサービスです。

既存のメッセージブローカーから移行するときに、既存コードや既存プロトコルを維持しやすいことが重要な特徴です。

例えばAmazon MQ for ActiveMQは、AMQP、MQTT、OpenWire、STOMPなどをサポートします。

したがって、

既存ActiveMQアプリケーション
+
OpenWireを使用
+
コード変更を最小化

なら、Amazon MQ for ActiveMQが強い候補になります。

一方、

AWS上で新規開発
+
単純な非同期作業キューが必要

というだけなら、既存ブローカー互換性は決定条件ではありません。

サービス名ではなく、既存アプリケーションが要求するメッセージング契約を確認します。

12. 5つのサービスを同じ判断軸で比較する

要件主な候補決定打
1件の作業を1つの論理処理系へ後から渡すAmazon SQS永続的な作業キュー
同じメッセージを複数の独立処理へ配信Amazon SNS発行・購読 / ファンアウト
各処理系に独立した未処理メッセージ保持も必要Amazon SNS + Amazon SQSファンアウト + 処理系ごとの障害分離
イベント内容に応じて処理先を選択Amazon EventBridgeイベントバス / イベントパターン
複数ステップの状態・分岐・待機を管理AWS Step Functions状態付きオーケストレーション
既存ブローカーのプロトコル互換が必要Amazon MQActiveMQ / RabbitMQ互換

すべてを「非同期サービス」として一括りにせず、それぞれが解く依存関係の形を見ます。

13. 非同期化しても下流処理自体の失敗は残る

疎結合化によって下流障害の即時伝播は抑えられますが、下流処理そのものが必ず成功するわけではありません。

SQSの再配信やデッドレターキューなど、採用したサービスに応じた失敗処理も合わせて設計します。

14. シナリオ1:下流サービスが停止しても注文を受けたい

要件:

注文API
↓
在庫更新

在庫更新は
数分停止する可能性あり

注文受付は継続したい

在庫更新は
復旧後でよい

ここでは、

注文API
↓
Amazon SQS
↓
在庫更新

を評価します。

決定打は、在庫更新完了を注文APIの成功条件にする必要がないことです。

15. シナリオ2:同じ注文イベントを複数システムが独立処理する

要件:

注文確定
↓
請求
通知
分析

どれか1つが停止しても
他を止めない

停止中の処理は
復旧後に追いつく

この場合、

注文
 ↓
Amazon SNS
 ├→ SQS 請求 → 請求処理
 ├→ SQS 通知 → 通知処理
 └→ SQS 分析 → 分析処理

を評価します。SNSが同じイベントをファンアウトし、各システムのSQSキューが個別に未処理メッセージを保持します。

16. シナリオ3:既存ブローカーを最小変更でAWSへ移す

要件:

既存ActiveMQ
↓
OpenWire
↓
多数の既存アプリケーション

大幅なコード変更は避けたい

この場合、

Amazon SQSへ
全面的に書き換える

ことを最初から前提にしません。

OpenWireをサポートするAmazon MQ for ActiveMQを評価します。

決定打は、既存メッセージング契約を維持する必要があることです。

17. 混同ポイント

混同正しい判断
非同期なら全部SQS作業キュー、ファンアウト、イベントルーティング、状態付きワークフローで候補が異なる
1つのSQSへ3コンシューマーを接続すれば全員へ同じメッセージが届く1つのキューは同じ仕事を分担するコンシューマー群として考える
SNSだけで各処理系に独立した長期未処理メッセージが残る個別保持が必要ならSNS + SQSを評価
フィルタリングが必要なら必ずEventBridgeSNSにもサブスクリプションフィルタリングがある
EventBridgeの再試行 = 長期間の作業キュー長期停止を吸収するならSQSなどの保持境界を評価
SQSへ送れば何日でも保持できるデフォルト4日、最大14日。停止時間だけでなく追いつき時間も考慮する
Step Functions = 高機能SQSStep Functionsは状態・分岐・待機を管理するワークフロー
キューを入れれば重複対策不要SQS標準キューでは重複配信を前提にべき等性を考慮
Amazon MQは常にSQSより優先既存ブローカー互換やプロトコル要件が決定打
疎結合化は必ず同期処理より優れるその場で下流結果が必要なら同期依存を残す場合がある

18. SAP-C02判断フロー

依存先が停止すると
呼び出し元も失敗するか?
↓
はい
↓
下流処理の結果を
その場で必要とするか?
├─ はい
│   ↓
│   同期依存を維持
│   ↓
│   その依存経路自体を
│   高可用化する
│
└─ いいえ
    ↓
非同期境界を作れる
    ↓
どの受け渡しが必要か?
    │
    ├─ 1つの作業を
    │  1つの論理処理系へ
    │        ↓
    │      Amazon SQS
    │
    ├─ 同じメッセージを
    │  複数の独立処理へ
    │        ↓
    │      Amazon SNS
    │
    ├─ 各処理系に
    │  独立した未処理保持も必要
    │        ↓
    │      SNS + SQS
    │
    ├─ イベント内容から
    │  処理先を選択
    │        ↓
    │      Amazon EventBridge
    │
    ├─ 複数ステップの
    │  状態・分岐・待機
    │        ↓
    │      AWS Step Functions
    │
    └─ 既存ブローカーとの
       プロトコル互換が必須
             ↓
           Amazon MQ

サービスを選んだ後も、

再試行
重複
順序
処理不能メッセージ
保持期間

を確認します。

まとめ

疎結合化の目的は、すべてを非同期にすることではありません。

まず、

下流結果を
その場で必要とするか

を確認します。

不要なら、

非同期境界を作れるか
↓
依存関係の形は何か
↓
Amazon SQS
Amazon SNS
Amazon EventBridge
AWS Step Functions
Amazon MQ
を選択

と進みます。

最も重要なのはサービス名ではなく、送信側と受信側の間にどのような契約が必要かです。

主要AWS公式資料

  • AWS Certified Solutions Architect - Professional 試験ガイド(SAP-C02)
  • AWS Well-Architected Framework — 疎結合の依存関係を実装する
  • Amazon SQS — 標準キュー
  • Amazon SQS — FIFOキュー
  • Amazon SQS — 可視性タイムアウト
  • Amazon SQS — キューのパラメータ設定
  • Amazon SNS — Amazon SQSキューへのファンアウト
  • Amazon EventBridge — ルール
  • Amazon EventBridge — イベント配信の再試行
  • AWS Step Functions — Step Functionsとは
  • Amazon MQ — Amazon MQとは
  • Amazon MQ — Amazon MQ for ActiveMQ

理解確認

このページの内容を9問で確認します。

理解確認9問へ →