非同期処理を使いこなそう !
-第 4 回 非同期処理の設計上の検討ポイント
山森 裕太
こんにちは、プロフェッショナルサービス本部アソシエイトクラウドアプリケーションアーキテクトの山森です。
7 月から連載が始まった「非同期処理を使いこなそう !」シリーズも早いもので第 4 回を迎えることとなり今回で最終回です。
みなさま、まずは今回までの連載はいかがでしたでしょうか。EC サイトを想定したワークロードを例に挙げ、 第 1 回 では非同期処理と同期処理の使い分けを説明しました。次に 第 2 回 では、非同期・同期処理のステップを説明し、実際に非同期処理を実現する上で必要な要素を解説しました。第 3 回 では、AWS サービスを活用した非同期・同期処理のアーキテクチャをお伝えしました。今回は、さらに一歩踏み込みで非同期処理を設計する上での検討ポイントを解説しながら AWS を活用したアーキテクチャをより良くしていきたいと思います。
この連載記事のその他の記事はこちら
- 選択
- 第 1 回 非同期処理ってなんだろう ?
- 第 2 回 非同期処理と同期処理の処理構造
- 第 3 回 AWS サービスを活用した非同期処理アーキテクチャ
- 第 4 回 非同期処理の設計上の検討ポイント
1. 非同期処理の設計上の検討ポイント
検討ポイントを挙げる前に、第 3 回 で示した AWS サービスを活用したアーキテクチャの中で、特に非同期処理の部分をおさらいします。
(1) EC サイトを利用しているエンドユーザーは、ブラウザやモバイルアプリの画面上から購入したい商品を選択し、注文処理サービスのインターフェースである Amazon API Gateway (以下、API Gateway) で提供されている API を呼び出します。
(2) 呼び出された API に対しての情報が注文完了トピックである Amazon SNS へとメッセージが送られます (その後 API Gateway は API レスポンスを返却します)。
(3) 配送・メール通知・請求/ポイント計算処理サービスの各 Amazon SQS へ注文完了トピックからメッセージが送信されます。
(4) AWS Lambda が各キューで受信したメッセージを自動でポーリング後、そのメッセージに対して処理を行います。
このアーキテクチャを題材として非同期処理での設計上の検討ポイントをみていきましょう。
検討ポイント | 説明 |
処理結果のレスポンス | 非同期処理での処理結果を返却するための API を検討する |
順序性 | キューイングされたメッセージやトピックの順序性の担保を検討する |
結果整合性 | 各種サービスでの処理におけるデータに不整合が発生した場合を検討する |
メッセージの滞留と鮮度 | 配信されるメッセージを適切な時間で処理できているか検討する |
ロギング | 非同期処理における各処理コンポーネントにおけるログ集約を検討する |
1-1. 処理結果のレスポンス
最初は処理結果のレスポンスです。
エンドユーザーがブラウザ画面から注文をリクエストした際のレスポンスからは注文処理が受け付けられたか否かしか判断できません。つまり、エンドユーザーは商品が配送されたかどうかや購入における請求が完了したかどうかなどがわかりません。そのため、各サービスの処理状況を返却するための API が必要になります。
エンドユーザーはブラウザ画面から対象 API へリクエストし、その結果をレスポンスとして受け取ることで状況を把握することができます。
エンドユーザーに対するユーザビリティを考慮すると、エンドユーザーによる操作ではなく自動的かつタイムリーに処理結果を表示されることが望ましいかもしれません。その際には API に定期的にポーリングを行うことが実現方法の一つとなります。ただし、バックエンドの負荷を考慮し、ユーザーアクションに応じて結果表示のための画面へ遷移した際に情報取得を行うほか、画面上に情報取得用ボタンを配置するなどの手段をとることもご検討ください。要件と必要なバックエンドのリソースのバランスも重要です。
各サービスにおける処理結果レスポンス
(クリックすると拡大します)
また、第 3 回 でも紹介されていますが、メール通知処理サービスのようにプッシュ型でエンドユーザーに通知するといった方法があります。加えて、チャットのようにニアリアルタイム性が求められる場合には、双方向通信プロトコルを実現する API Gateway での WebScoket や AWS AppSync でのサブスクリプション 利用も検討ください。
1-2. 順序性
次は順序性です。非同期処理では各サービスが独立して処理を実施するため、それぞれのサービスに対して一貫性のある順序にもとづいた連携を行うことは困難です。さらに、今回のアーキテクチャで利用する Amazon SQS や Amazon SNS に焦点を絞った場合、標準キュー・標準トピック や FIFO (First in First out) キュー・FIFO トピック といったセマンティクスの選択にも影響を与えます。標準キュー・標準トピックを選択した場合のメッセージの配信では順序性ベストエフォートとなり、FIFO キュー・FIFO トピックでは厳密な順序性が担保されます。そのため、メッセージに対して送られてきた順に処理を実行したい場合、順序性を担保したセマンティクスを検討する必要があります。
Amazon SQS 標準キューと Amazon SNS 標準トピックの順序性
(クリックスト拡大します)
ただし、順序性を検討する際は「ビジネス上、順序を守らないといけない理由」を再度確認してみてください。順序性を保つための仕組みを導入すると開発・運用コストやメンテナンス性に影響がでることがあります。したがって、ビジネス上必須でなければ、シンプルなアーキテクチャを目指し、過度な複雑さを回避することも選択肢となります。
ここでは、順序性を保つための例として、先着順でポイントを付与するポイント計算処理サービスを考えてみます。商品購入者への通常のポイント付与に加えて、対象商品を購入した先着 1000 名のユーザーにはさらにポイントを付与するとしましょう。ただし、キャンペーンの追加ポイントの合計は 100 万ポイントとします。
このような場合に、順序性が担保されていないと、購入順序でいえば先だったユーザーがポイント計算処理サービスにおいては最終的に合計ポイントに達してしまい追加ポイントが付与されないというケースが存在してしまいます。
このような場合にメッセージに関して順序性を担保するには、Amazon SNS の FIFO トピックや Amazon SQS の FIFO キューの利用が挙げられます。ただ、ここで注意いただきたい点はスループットです。例えば、Amazon SQS の FIFO キューを選択した場合に順序性は担保されますが、一方で 1 秒あたりのトランザクション数 (TPS) が制限されます。
Amazon SQS FIFO キューと Amazon SNS FIFO トピックの順序性
(クリックすると拡大します)
詳細は メッセージに関するクォータ のメッセージのスループットを確認ください。ただし、一部リージョンでは FIFO キューの高スループット も対応し始めており、今後選択肢の幅を広げてくれるかもしれません。さらに、コンシューマが処理できないメッセージがあった際には以降のメッセージに対する処理がブロックされてしまいます。以下「メッセージの滞留と鮮度」でお伝えするメッセージの待避によって対応できますが、待避されたメッセージの順序性も検討いただければと思います。
また、第 3 回 で紹介された Amazon Kinesis Data Streams など別の AWS サービスを利用することでもこの問題に対応することができます。
1-3. 結果整合性
結果整合性 (Eventual Consistency) とは、分散システムの各データストアに対してある項目のデータ更新が発生した場合に、それぞれのデータストアで一時的な不整合は発生しつつも、最終的には一貫性のある状態になる性質を意味します。
今回のワークロードではファンアウトされたメッセージに対して各サービスのコンシューマとなる AWS Lambda がデータストアの Amazon DynamoDB テーブルを更新しますが、これらに対して結果整合性を検討する必要があります。また、データストアの更新処理が正常に実施されずに、恒久的なデータ不整合が発生してしまう場合があります。これを解消する手段のひとつに補償 (補正) トランザクション (Compensating Transaction) があります。補償トランザクションでは、データ不整合が発生した場合に各データストアに対して打ち消し処理を行うためのイベント (今回のアーキテクチャでは、Amazon SNSやAmazon SQSにおけるメッセージ) を発行し、このイベントに対してコンシューマが一貫性のあるデータの状態にする処理をことができます。
1-3-a. コレオグラフィにおける結果整合性
ここまで、結果整合性や補償トランザクションといったコンセプトを説明しました。次は、今回のアーキテクチャにおける補償トランザクションの導入を解説していきます。
今回のアーキテクチャのように、あるイベントがファンアウトされてそれぞれのサービスが独立した処理を行うものをコレオグラフィ (Choreography) と呼びます。
コレオグラフィでは、(1) あるデータストア (例えば、請求処理サービスの Amazon DynamoDB) でデータ不整合が発生した場合にそのイベントを Amazon SNS でメッセージとして受け取ります。(2) この Amazon SNS のメッセージをそれぞれのサービスが受信することで、打ち消し処理を実行します。これにより、各データストアでのデータの一貫性が担保されます。
コレオグラフィにおける結果整合性を実現するアーキテクチャ
(クリックすると拡大します)
1-3-b. オーケストレーションにおける結果整合性
結果整合性を実現するためのアーキテクチャとしてオーケストレーションを紹介します。オーケストレーションとは、各種サービスにおける処理の管理をつかさどるコンポーネントを役割として置くアーキテクチャとなり、文字通り指揮者がそれぞれの演奏者を導いていくようなコンセプトとなります。
今回のワークロードでは、注文処理サービスを起点として動作する配送・メール通知・請求・ポイント計算といったサービスがオーケストレーションとしての管理対象となります。このオーケストレーションでは、各サービスでの処理状況を管理しているため、いずれかのサービスのデータストアにて不整合が発生した際、打ち消し処理を組み込むことができます。
このオーケストレーションを実現するための AWS サービスには、AWS Step Functions があります。AWS Step Functions 自体の説明はここでは割愛しますが、これを利用することで、例えば、(1) 配送処理サービスは正常終了、(2) メール通知処理サービスが正常終了、(3) 請求処理サービスでデータ不整合があった際には、(4) 請求処理の前段で行われていた処理の打ち消しを実施する、といったワークロードを実現することができます。
オーケストレーションにおける結果整合性を実現するアーキテクチャ
(クリックすると拡大します)
1-4. メッセージの滞留と鮮度
次はメッセージの滞留と鮮度です。非同期処理では処理の契機となるメッセージが「適切な時間内で処理されているか」、「もう処理する必要の無い古いメッセージが残存していないか」ということが重要となってきます。そのため、該当メッセージが滞りなく処理されていることの確認や古いメッセージに対しての対応が必要となります。
まず、古いメッセージへの処理を回避するためにもメッセージの保存期間を適切に設定してください。例えば、Amazon SQS では メッセージの保持期間 として 1 分から最大 14 日間までメッセージを保持するように設定できます。また、この設定により滞留を防ぐことにも繋がります。
滞留が続くようであれば、バックエンドの増強などの検討が必要となってくるかもしれません。しかし、この滞留の原因はバックエンドのパフォーマンスによるものだけとは限りません。
例えば、Aamazon SQS から受信したメッセージが想定外のデータ形式により処理が失敗したり、連携するシステムやサービスにてリクエストを受け付けなかったり、そもそもアプリケーションの不具合等、様々なケースがあります。メッセージが処理されずに滞留するケースは様々な原因があるため、滞留によるスループットの低下や無駄なリソース消費を避けるためにも、メッセージの待避を検討する必要があります。
メッセージを待避する目的は、滞留によって発生するスループット低下の回避に加えて、不正なメッセージの原因解析、失敗したことを通知させるためなども挙げられます。そして、このメッセージの待避を実現するのが デッドレターキュー (Dead Letter Queue) です。デッドレターキューを利用することで、あらかじめ設定したリトライの上限回数を超過したメッセージを、別のキューとなる Amazon SQS に移すことができます。また、デッドレターキューに入ったメッセージに対して適切なデータ形式に修正し、それを元のキューに送信し直して処理を再度実行することが可能となったりします。
Amazon SQS や Amazon SNS におけるデッドレターキュー
(クリックすると拡大します)
1-5. ロギング
最後はロギングです。もちろん、各コンポーネントでログ出力は対応済みだと思います。ただし、非同期処理では分散コンポーネントであるが故に、出力されたログを使ってワークロードに対し一貫性のある処理状況の把握や管理が困難となります。そのためログ集約の検討が必要となってきます。このログ集約で重要となってくるのはログのデータ容量や出力リクエストに対するスケーラビリティが高いストレージの選択です。AWS では Amazon CloudWatch や Amazon S3 が候補となってくるのではないでしょうか。
ただし、Amazon CloudWatch と Amazon S3 にはそれぞれ異なる特性があります。例えば、Amazon CloudWatch はリアルタイムでログ参照や検索を実施するのが比較的簡単ですがログ分析などは不向きです。一方、Amazon S3 はログとして保存されているオブジェクトをダウンロードする、もしくは Amazon Athena などその他サービスを介して参照・検索を実施しなければなりませんが、ログ分析用のデータソースとして他 AWS サービスとの連携をサポートしています。
そして、もうひとつ重要となってくるのが分散トレーシングです。分散トレーシングとは一連の処理をユニークに識別するためのトレース ID を発行し、トレース ID をもとに各サービスでのログなどを統合・可視化することを意味します。先述のとおり、非同期処理処理ではそれぞれのコンポーネントが処理を実施するために、ワークロードをエンドツーエンド (End to End、E2E) で把握・管理することが難しいです。そのため、この分散トレーシングが重要となってきます。この分散トレーシングを導入する場合、AWS サービスでは AWS X-Ray が役立ちます。一連の処理におけるコンポーネントの繋がりを可視化したり、ターンアラウンドタイム (Turn Around Time、TAT) や各コンポーネントにおける処理時間の表示、詳細なログデータへのドリルダウンの提供、サンプリングによる取得量調整といったアプリケーションパフォーマンス管理 (Application Performance Management、APM) の機能を享受することができます。また、AWS X-Ray を利用した場合、自主的に発行しなければならないトレース ID を自動的に管理してくれます。
ロギングや分散トレーシングの導入によって、みなさまが非同期処理で直面するワークロード上の様々な問題に対応するため強力なサポートを提供してくれるはずです。そして、この情報をもとに原因調査や対策の実施、アーキテクチャのさらなる改善に役立てていただければと思います。
1-6. 非同期処理における考慮事項のまとめ
非同期処理における考慮事項と題して 5 つのポイントに絞った内容をお伝えしました。
最後に、今回のアーキテクチャで利用されている Amazon SQS や Amazon SNS において補足的な内容を説明していきます。
2. Amazon SQS、Amazon SNS のセマンティクス
非同期処理とは少し観点がズレますが、今回のような Amazon SQS や Amazon SNS を利用したアーキテクチャにて標準キューや標準トピックを選択した場合、メッセージは少なくとも 1 回 (At-least once) の配信となります。そのため、同一メッセージが 2 回以上配信された場合にも同一の結果を保証しなければなりません。これを 冪等性 (idempontence) と言います。配送処理サービスを例にとって考えると、もし同一メッセージが 2 回以上配信された場合に同じ購入商品が 2 回届いてしまう可能性があります。そのため、標準キューや標準トピックを利用した際にも冪等性を担保されるアーキテクチャを考えていきます。
2-1. 処理開始時に重複処理をチェックする
この方法を実施するためには、前提として Amazon DynamoDB のテーブルにおけるアイテムにはメッセージを特定するため、Amazon SQS におけるメッセージ ID などの情報が格納されている必要があります。その上で、(1) メッセージ ID によりテーブル内のアイテムに対して重複実行のチェックを行う。(2) 重複実行の恐れがなければ (同一メッセージ ID のアイテムがテーブル内に存在しない)、テーブルにアイテムを Put して、外部システムへの情報連携を実施する。(2’) 重複実行であれば (同一メッセージ ID のアイテムがテーブル内に既に存在)、外部システムへの情報連携を実施しない。
重複実行チェックにおける冪等性の担保
(クリックすると拡大します)
2-2. 重複処理が発生しても影響の無いアーキテクチャにする
この方法を実現するための 1 つの手段として Amazon DynamoDB Streams を利用するパターンを解説します。(1) AWS Lambda 関数は従来通りアイテムをテーブルに Put しますが、外部システムへの情報連携は行いません。(2) Amazon DynamoDB Streams をイベントソースとして AWS Lambda 関数が外部システムへの連携を行います。ここで重要となるのは、Aamazon DynamoDB Streams は項目の追加、変更、削除をイベントとして検出するという点です。そのため、Amazon DynamoDB のテーブルへ Put する AWS Lambda 関数は重複処理を行ったとしても、アイテムの内容が同じであれば、2 回目以降は Amazon DynamoDB Streams のイベントが発生しません。
Amazon DynamoDB Streams による冪等性の担保
(クリックすると拡大します)
ここでは Amazon SQS や Amazon SNS で標準キューや標準トピックを利用していた場合の冪等性の担保についてお伝えしましたが、FIFO キューや FIFO トピック、Amazon Kinesis Data Streams では必ず 1 回 (Exactly-Once) の配信が担保されます。そのため、これらを利用することでメッセージブローカー側に冪等性担保の対応策を講じることも可能となります。
また、AWS Lambda などのリトライが発生した際にも重複処理が行われる可能性があるので、同様に冪等性の検討を実施いただければと思います。
3. まとめ
いかがでしたでしょうか。最終回となる今回は、非同期処理をさらに使いこなしていただくための設計上の検討ポイントとその対応内容を解説しました。
同期処理と比較すると、非同期処理は各サービスが独立性をもって稼働しているため、設計における検討事項はより複雑になりますが、一方で、拡張性や可用性、さらにサービスをデプロイする上での保守性など多くのメリットを受け取ることができます。今回説明した内容が、非同期処理を導入する上での一助になれば嬉しく思います。
最後になりますが、「非同期処理を使いこなそう !」と題した全 4 回の連載にお付き合いいただきありがとうございました。
この連載記事のその他の記事はこちら
- 選択
- 第 1 回 非同期処理ってなんだろう ?
- 第 2 回 非同期処理と同期処理の処理構造
- 第 3 回 AWS サービスを活用した非同期処理アーキテクチャ
- 第 4 回 非同期処理の設計上の検討ポイント
筆者プロフィール
山森 裕太
アマゾン ウェブ サービス ジャパン合同会社
プロフェッショナルサービス本部 アソシエイトクラウドアプリケーションアーキテクト
サーバーレスを中心としたクラウドネイティブアプリケーションにより、アジリティの高いシステムの設計・開発を支援しています。
最近は完全栄養食品を毎日食べる生活を送っています。
AWS を無料でお試しいただけます