【セッションレポート】同期という思い込み 世界は非同期で構成されている(AWS-56)#AWSSummit
はじめに
AWS Summit Japan 2024 に参加しました。
「同期という思い込み 世界は非同期で構成されている」のセッションレポートです。
セッション概要
サーバーレスサービスである API Gateway や Lambda を使って REST API を構築されている方は多いのではないでしょうか。サーバーレスで REST API をつくると、サーバーの確保作業や設定の手間から解放され、デフォルトの可用性が与えられるなどメリットは大きいです。そして、その効果を最大化させるのが非同期アーキテクチャです。サーバーの呪縛から解放されるサーバーレスの真価はここにあります。このセッションではさまざまなサーバーレスの非同期パターンを紹介するとともに、実際の顧客事例においてどのようなユースケースで非同期系サーバーレスが利用されているかをご紹介いたします。
セッションスピーカー:下川 賢介
所属:アマゾンウェブサービスジャパン合同会社 サービス スペシャリスト統括本部 アプリケーション開発技術本部
アジェンダ
- 身近な話から
- 非同期が求められる理由
- 非同期について
- 世界は非同期で構成されている
セッションレポート
身近な話から
- 日常の非同期になっているもの
- 宅配
- 荷物を届ける→置き配←帰宅時に荷物を受け取る
- 宅配
非同期が求められる理由
- イノベーションループ
- ビジネスアイデア
- 実験
- 傾聴(1. ビジネスアイデンティへ)
- アーキテクチャ結合度
- 小さなピースに分けてゆるく結合させたい = マイクロサービス
- 小さなピースをAPIで結合してみる
- デリバリー速度の獲得
- マイクロサービスのメリットは、ビジネス変化に対応すること = デリバリー速度の獲得
- ただし、APIは強いインターフェース契約があるため、APIサービスは頻繁な変更の対応が難しい
- マイクロサービスのメリットは、ビジネス変化に対応すること = デリバリー速度の獲得
API利用者と提供者は、互いに依存する
- API利用者はAPI提供者の公開内容に従って送信する必要がある。契約を守る
- API提供者は、契約を破棄できない
- JSONスキーマを変えられない
仕様と実装の乖離が許されないAPI(契約)
- API 仕様書(Swaggerなど)
- 契約は公開できるが、クライアントに対して強制は難しい
ビジネスが大きくなると、多様化、複雑化する依存関係となる。
同期的なAPIの課題
- 他のサービスの障害に影響を受ける
- クライアントへの統一して提供が難しい
- 高スループット対策
- スロットリング対策
- HTTPリソースやメソッドごとに要件が複雑になる
デリバリー速度の獲得に向けて、疎結合にマイクロサービスを組み立てるなら、非同期にゆるく結合してみては?という提案。
APIにおける様々な解決
本セッションでは、非同期と同期を対比構造で説明するが、本来同期と非同期は、排他の関係ではなく、適材適所でそれぞれ利用する。
非同期について
同期と非同期のアーキテクチャパターンの違い
- 同期(API)
- クライアント → サービスA → サービスB
- サービスB → サービスA → クライアント
- 非同期(イベント)
- クライアント → サービスA
- サービスA → クライアント、サービスA → サービスB
結合度を実例で確認
- 同期的なAPIの課題
- クライアント → プロデューサー(受注サービス)→ コンシューマ(発送サービスと会計サービス)
- 発送サービスが障害になると、プロデューサにも影響を受ける。
- 複数サービスが障害ポイント
- サービスクオリティが低いものに引きづられる形で、全体のサービス品質が決まる
- コンシューマに在庫サービスを追加した場合
- プロデューサにもコード修正が必要になる。同期的にリリース作業しなければならない
- 発送サービスが障害になると、プロデューサにも影響を受ける。
- クライアント → プロデューサー(受注サービス)→ コンシューマ(発送サービスと会計サービス)
- 非同期イベント
- クライアント → プロデューサ(受注サービス)→ イベント ← コンシューマ(発送サービスと会計サービス)
- 各サービスは独立している。プロデューサとコンシューマは疎結合である
- 品質、可用性はコンポーネントごとに設計できる
- 発送サービスが障害になっても、他のサービスは影響を受けにくい
- コンシューマに在庫サービスを追加した場合
- プロデューサはコード修正不要
- クライアント → プロデューサ(受注サービス)→ イベント ← コンシューマ(発送サービスと会計サービス)
世界は非同期で構成されている(顧客事例を添えて)
4パターンを紹介する
- 非同期レスポンスパターン
- モバイル決済
- トラフィックのコントロール
- 段階的なキャッシュ生成
- リアルタイムリコメンデーション
- 非同期による分割統治
- セキュアなアップローダー
非同期レスポンスパターン
非同期では、クライアントがサービスAから受け取るのは、実際の処理結果ではない。どのようにサービスBの処理結果を受け取るのか?
- 非同期(イベント)
- クライアント → サービスA
- サービスA → クライアント、サービスA → サービスB
モバイル決済の構成
- クライアントからのモバイル決済 → SQS → Lambda → API Gateway → WebSocket → 店舗POS
- フロントエンドのリクエストをLambdaで処理して、API Gatewayと店舗POSはWebSocketで接続されており、応答を返している
モバイル決済におけるイベント駆動の決済完了通知サービス
- 同期箇所)アプリ → 決済処理システム → API Gateway → Lambda → DynamoDB(決済完了内容を保存)
- 非同期箇所)DynamoDB Steams → Lambda → API Gateway → WebSocketによってアプリに通知
- 非同期では、クライアントがサービスAから受け取るのは、実際の処理結果ではない。どのようにサービスBの処理結果を受け取るのか?
- WebSocketによるサーバーサイドプッシュで返せる
API と WebSocketによる応答性向上
- クライアント → API Gateway → SQS → Lambda(API Gatewayを介してクライアントにサーバーサイドプッシュ)
- クライアントからAPI Gateway経由で常にWebSocket接続されている
- クライアントからAPI Gatewayは同期。SQS以降は非同期である
トラフィックのコントロール
- プロビジョン
- 予想したリクエスト量より少ないと、余剰リソースになる
- 予想したリクエスト量より多いと、さばけない
- オートスケール
- タイムラグにより、リクエストが落ち着いているが余剰リソースとなることがある
- サーバレス
- リクエストに応じてスケールさせることができる
店舗での音楽のチャンネル選曲とプレイリスト配信サービス
- 店舗の雰囲気や時間帯、天候に合わせたBGMを提供する
- データセンターの楽曲管理や配信処理 → API Gateway → SQS → Lambda
トラフィックの方向性は2つある
- クライアントからサービスまでのトラフィック
- ここを深堀りする
- サービス間のトラフィックコントロール
-
クライアント → API Gateway → トラフィックの終端
- クライアントからサービスまでのトラフィックにおいて、トラフィックの終端をできるだけクライアントに寄せる
- キューやストリームでトラフィックを終端させて、HTTP status 202を返却する
- クライアント → SQS → API Gateway → トラフィックの終端
- CDNを前段に配置しキャッシュを返却する
- クライアント → CloudFront → API Gateway → トラフィックの終端
段階的なキャッシュ生成
- キャッシュから応答できていれば高速なレスポンスになる
- キャッシュ切れの場合、応答が遅延する
段階的なキャッシュ生成
- キャッシュ更新指示を受けるキュー
- 同期リクエスト応答には、有効期限切れオブジェクトを返しつつ、SQSに更新命令する
- SQSからトリガーされたLambdaが非同期でキャッシュを最新化する
- 次回以降のキャッシュヒットでは、有効期限内のオブジェクト応答となる
- 厳密に最新さを求めらない場合に有効
- 同期リクエスト応答には、有効期限切れオブジェクトを返しつつ、SQSに更新命令する
段階的なキャッシュ生成の構成
- キャッシュ有効期限内のオブジェクト応答)
- クライアント→ API Gateway → Lambda → キャッシュ
- 「クライアント→ API Gateway → Lambda → キャッシュ」 は同期
- クライアント→ API Gateway → Lambda → キャッシュ
- キャッシュ有効期限切れのオブジェクト応答)
- クライアント→ API Gateway → Lambda → SQS → Lambda → データストアで最新データを取得 + キャッシュを更新
- 「SQS → Lambda → データストアで最新データを取得 + キャッシュを更新」」は非同期
- クライアント→ API Gateway → Lambda → SQS → Lambda → データストアで最新データを取得 + キャッシュを更新
非同期による分割統治
分割統治は、依存関係を少なくして、コンポーネントを分離し、それぞれを自律的に統治すること
小さくて大量のタスクを捌きたい
- 分散処理
- 再試行の管理を手軽にしたい
非同期による分割統治の構成例
- S3 → SNS → SQS → Lambda → S3 → SNS → SQS → Lambda → S3 → SNS → SQS → .....
- 数珠つなぎにアーキテクチャを構成
- 「S3 → SNS → SQS → Lambda」のコンポーネントにおいて、独立してスケールできる。 = 分割統治
- Step Functions
- 数珠つなぎのアーキテクチャ = Step Functionsワークフローでも実現できる
- ワークフローで各コンポーネントのリトライ処理やエラーハンドリングが対応できる
- 軽量 Map Reduceは、Step Functionsで対応できる
- 数珠つなぎのアーキテクチャ = Step Functionsワークフローでも実現できる
セキュアなアップローダー
アップローダーとしての非同期パターン
- API Gateway
- API Gatewayにはペイロードサイズ10MB制限がある
- API Gatewayは API操作の同期的なread/commandに専念してもらう
- 署名付きURLを発行することで、S3にアップロード可能
- write / uploadは、S3を対象にすることでスケール
- 署名付きなのでセキュア
- 署名付きURLを取得できるクライアントは、API Gatewayで認証済み
構成
- クライアント → API Gateway → Lambda → S3(署名付きURLを発行)
- ここは同期である
- クライアントは、S3にファイルをアップロードできる
- アップロード後、非同期に「S3 → SQS → Lambda」で処理される
まとめ
- API Gateway → Lambdaでの同期パターン以外に、サーバレス非同期パターンも重要である
- 同期と非同期は、排他的ではなく、組み合わせて使えるもの
- 色々な企業ですでに非同期アーキテクチャが導入されている
- 非同期は、障害影響の分離、デプロイ依存関係の分離も可能。
非同期処理は、ありふれたアーキテクチャパターンである。
感想
このセッションを通じて、非同期アーキテクチャが持つ多くのメリットを再認識しました。
特に、サーバーレス環境における非同期処理の有効性についてです。非同期アプローチが、デリバリー速度の向上、障害影響の分離、そしてシステムの柔軟性と拡張性の向上に寄与することが分かりました。
同時に、同期と非同期を状況に応じて適切に使い分けることの重要性も強調されており、バランスの取れた設計アプローチの必要です。
これにより、ビジネスの急速な変化に迅速に対応しつつ、サービス間の疎結合を実現する強力なツールとしての非同期アーキテクチャの価値を改めて認識しました。
セッションでは、WebSocketを活用したサーバーサイドプッシュや、段階的なキャッシュ生成など、具体的な実装パターンが紹介され、実際のシステム設計に活かせるヒントを多く得ることができたと思います。
また、非同期処理が単なる技術的な選択肢だけでなく、ビジネスの俊敏性や顧客体験の向上にも直結する重要な要素であることを、様々な顧客事例を通じて学ぶことができました。