Amazon Web Services ブログ
Operating Lambda: イベント駆動型アーキテクチャを理解する – Part 1
Operating Lambda シリーズでは、AWS Lambda ベースのアプリケーションに関わる開発者、アーキテクト、システム管理者向けの重要なトピックについて説明します。この3部構成のシリーズでは、イベント駆動型アーキテクチャと、それがどのようにサーバーレスアプリケーションと関連しているかについて説明します。
Part 1 では、イベント駆動型パラダイムの利点と、それがスループット、スケール、拡張性をどのように改善できるかについて説明します。また同時にアプリケーションの複雑性とコードの総量を削減する点についても説明します。
イベント駆動型アーキテクチャは現代の組織で一般的に利用される、複雑なシステムの構築に伴う課題に対処するのに役立つため人気が高まっています。このアプローチはマイクロサービスの利用を促進します。マイクロサービスは、小さく、狭い領域の機能セットを実行する特殊なサービスです。適切に設計された Lambdaベースのアプリケーションは、マイクロサービスアーキテクチャの原則と互換性があります。
Lambda はイベント駆動パラダイムにどのように適合するか
Lambda は、イベントに応じてカスタムコードを実行するオンデマンドのコンピュートサービスです。ほとんどの AWS サービスはイベントを生成し、その多くは Lambda のイベントソースとして機能します。Lambda では、コードはコードデプロイメントパッケージに格納され、イベントハンドラが含まれています。コードへのすべての相互作用は Lambda API を介して行われ、サービスの外部から関数を直接実行することはありません。Lambda 関数の主な目的は、イベントを処理することです。
従来のサーバーとは異なり、Lambda 関数は常に実行はされません。関数がイベントによってトリガーされる時をinvocationと呼びます。Lambda 関数の実行時間は意図的に 15 分に制限されますが、AWS のすべてのお客様において、ほとんどの呼び出しは平均して 1 秒未満で実行されます。計算集中的なオペレーションでは、1 つのイベントを処理するのに数分かかる場合がありますが、ほとんどの場合、実行時間は短くなります。
Lambda 関数をトリガーするイベントは、Amazon API Gateway経由の HTTP リクエスト、Amazon EventBridge ルールによって管理されるスケジュール、または Amazon S3 通知など、ほとんど何でもかまいません。最も単純な Lambda ベースのアプリケーションでも、少なくとも 1 つのイベントを使用します。
イベント自体は、何が発生したかに関する情報を含む JSON オブジェクトです。イベントは、システム状態の変化に関する事実であり、不変であり、それが発生した時間は重要です。すべての Lambda ハンドラの最初のパラメータにはイベントが含まれます。イベントは、eコマースアプリケーションで生成された新しい注文など、別のマイクロサービスから独自に生成することができます。
またイベントは、キューで新しいメッセージが利用可能になったときにAmazon SQS などの AWS サービスによって生成することもできます。
いずれの場合も、イベントは Lambda ハンドラの最初のパラメータとして Lambda 関数に渡されます。
- ハンドラの外部のコード (「初期化」コードとも呼ばれます) は、ハンドラの前に実行されます。これはライブラリのインポートやグローバルオブジェクトの宣言と、初期化などのタスクに使用されます。
- ハンドラ自体はイベントオブジェクトを受け取る関数です。Lambda 関数で使用されるランタイムに関係なくイベントは JSON オブジェクトです。
小規模なアプリケーションの場合、イベント駆動型アプリケーションとリクエスト駆動型アプリケーションの違いは明確ではない場合があります。アプリケーションがより多くの機能を開発し、より多くのトラフィックを処理するにつれて、この違いはより明確になります。リクエスト駆動型アプリケーションは、通常ダウンストリーム関数に直接命令を発行してアクティビティを完了し、多くの場合両者は密結合となっています。イベント駆動型アプリケーションは、他のサービスやシステムによって監視可能なイベントを作成しますが、イベントプロデューサはイベントを購読しているコンシューマを認識しません。通常両者は疎結合です。
ほとんどの Lambda ベースのアプリケーションは、データを永続的に保存し、他のシステムやサービスと統合するために AWS サービスを組み合わせて使用します。これらのアプリケーションではLambda はサービス間の糊として機能し、サービス間で渡されるデータを変換するビジネスロジックを提供します。
Lambda ベースのアプリケーションを構築するには、イベントベースアーキテクチャの構築に関する多くのベストプラクティスに従います。イベント駆動型システムの構築を支援するために、多くの開発アプローチが登場しました。ドメイン駆動設計 (DDD) のための対話的なアプローチであるイベントストーミングは、有名な方法論の 1 つです。ワークロード内のイベントを調査し、それらを境界コンテキストとしてグループ化することで、アプリケーション内で境界付けられたマイクロサービスを開発することできます。
イベント駆動型アーキテクチャの詳細については、「What is an Event-Driven Architecture?」と「What do you mean by Event-Driven?」を参照してください。
イベント駆動型アーキテクチャの利点
ポーリングと Webhook をイベントに置き換える
多くの従来のアーキテクチャは異なるコンポーネント間の状態を確認するために、ポーリングと Webhook メカニズムを頻繁に使用します。ポーリングは新しいデータが利用可能になり、ダウンストリームサービスと同期するのに遅延が発生するため、更新情報の取得が非常に非効率的になる可能性があります。Webhook は、統合したい他のマイクロサービスによって常にサポートされているとは限りません。また、カスタムの認証と認可の設定が必要になる場合もあります。どちらの場合も、これらの統合方法は、開発チームによる追加作業なしにオンデマンドでスケールすることは困難です。
これらのメカニズムはどちらもイベントに置き換えることができます。イベントはフィルタリング、ルーティングが可能で、ダウンストリームに送信されてマイクロサービスに消費されます。このアプローチはネットワーク帯域の消費量、CPU 使用率を削減し、潜在的にコストの低下につながります。これらのアーキテクチャは個々の機能単位がより小さくなり、しばしばコード量も少なくなるので複雑さを低減することができます。
イベント駆動型アーキテクチャはまた、リアルタイムに近いシステムの設計が容易になり、バッチベースの処理を取り除きやすくなります。イベントは、アプリケーションの状態が変更されたときに生成されます。したがって、マイクロサービスの実装コードは、単一のイベントを処理するように設計する必要があります。スケーリングは Lambda サービスによって処理されるため、このアーキテクチャは実装コードを変更することなく、トラフィックの大幅な増加を処理できます。イベントがスケールアップすると、イベントを処理するコンピュートレイヤもスケールします。
複雑さの軽減
マイクロサービスによって、開発者やアーキテクトは複雑なワークフローを分解することが可能になります。例えば、eコマースのモノリスアプリケーションは、在庫、受注と配送、会計の各サービスを使用して、注文の受け付けと支払いのプロセスに分解することができます。管理とオーケストレーションが複雑になるかもしれないモノリスなアプリケーションが、イベントメッセージと非同期的に通信する一連の分離されたサービスになります。
このアプローチはまた、異なるレートでデータを処理するサービスを組み立てることも可能になります。この場合、注文の受け付けマイクロサービスは、Amazon SQS キューにメッセージをバッファリングすることで大量の注文を保存できます。
支払処理サービスは通常、支払処理の複雑さのために低速ですが、SQS キューから安定したメッセージの流れを取得することが可能です。AWS Step Functions を使用して、複雑な再試行とエラー処理ロジックをオーケストレーションすることが可能で、数十万の注文のための活発な支払いワークフローを実行できます。
スケーラビリティと拡張性の向上
マイクロサービスは、通常 Amazon SNS や SQS などのメッセージングサービスに発行されるイベントを生成します。これらはマイクロサービス間の伸縮性のバッファーのように動作し、トラフィックが増加したときにスケーリングを処理するのに役立ちます。EventBridgeのようなサービスは、イベントの内容に応じてメッセージをフィルタリングやルーティングすることが可能で、ルールとして定義されます。その結果、イベントベースのアプリケーションはモノリシックなアプリケーションよりもスケーラブルで高い冗長性を提供します。
このシステムは拡張性も高く、他のチームは注文処理や支払い処理マイクロサービスに影響を与えることなく、機能を拡張 、追加することができます。EventBridge を使用してイベントを発行することにより、このアプリケーションは在庫管理マイクロサービスなどの既存のシステムと統合されますが、将来別のアプリケーションをイベントコンシューマとして統合することもできます。イベントプロデューサはイベントコンシューマに関する知識を持つ必要がないため、マイクロサービスのロジックを簡素化できます。
詳細については、「How event-driven architecture solves modern web app problems」および「How to Use Amazon EventBridge to Build Decoupled, Event-Driven Architectures」を参照してください。
イベント駆動型アーキテクチャのトレードオフ
変化するレイテンシ
すべての処理を単一デバイス上の、同一メモリ空間内で実行することもあるモノリシックアプリケーションとは異なり、イベント駆動型アプリケーションはネットワーク経由で通信します。この設計は、変化するレイテンシを引き起こします。アプリケーションを設計してレイテンシを最小化することも可能ですが、モノリシックアプリケーションはほとんど常に、スケーラビリティと可用性を犠牲にして、レイテンシをより低減するように最適化されます。
AWS のサーバーレスサービスは高い可用性を備えており、リージョン内の複数のアベイラビリティーゾーンで動作します。サービスが中断した場合、サービスは自動的に異なるアベイラビリティーゾーンにフェイルオーバーし、トランザクションを再試行します。その結果、トランザクションが失敗する代わりにトランザクションは正常に完了しますが、レイテンシは高くなります。
銀行での高頻度の取引アプリケーションや倉庫での1ミリ秒未満のロボティクスオートメーションなど、一貫した低レイテンシのパフォーマンスを必要とするワークロードはイベント駆動型アーキテクチャには適していません。
結果整合性
イベントは状態の変化を表します。特定の時点において、多数のイベントがアーキテクチャ内のさまざまなサービスを通過するので、このようなワークロードの多くは結果整合性になります。これによりトランザクションを処理したり、重複を処理したり、システムの全体的な状態を正確に決定する処理がより複雑になります。
ACID 属性が要求される一部のワークロードは、イベント駆動型アーキテクチャには適していません。しかし多くのワークロードには、結果整合性の要件 (たとえば、現在の時間の総注文数) または強い一貫性 (現在の在庫など) の要件の組み合わせが含まれています。強力なデータの整合性を必要とする機能については、これをサポートするいくつかのアーキテクチャパターンがあります。
イベントベースのアーキテクチャは大量のデータではなく、個々のイベントを中心に設計されています。一般的に、ワークフローは複数のイベントを同時に操作するのではなく、個々のイベントまたは実行フローのステップを管理するように設計されています。リアルタイムイベント処理は、イベント駆動型システムのバッチ処理で好んで利用され、バッチを多数の小さな追加の更新に置き換えます。これにより、ワークロードの可用性と拡張性が向上しますが、イベントが他のイベントを認識することは難しくなります。
呼び出し側に値を返す
多くの場合、イベントベースのアプリケーションは非同期です。つまり、呼び出し側のサービスは、他のサービスからの要求を待たずに他の作業を続行します。これはスケーラビリティと柔軟性を実現するイベント駆動型アーキテクチャの基本特性です。つまり戻り値やワークフローの結果の受け渡しは、多くの場合、同期実行フローよりも複雑になります。
本番システムにおける多くの Lambda関数の実行は非同期で、S3 や SQS などのサービスからのイベントに応答します。このようなケースでは、多くの場合イベントの処理の成功または失敗が値を返すことよりも重要です。Lambda のデッドレターキュー (DLQ) などの機能が提供されており、呼び出し元に通知することなく、失敗したイベントを特定して再試行できます。
ウェブアプリケーションやモバイルアプリケーションなどのインタラクティブなワークロードの場合、エンドユーザーは通常、トランザクションの戻り値、または現在のステータスを受け取ることを期待します。これらのワークロードには、呼び出し元にリッチイベントを提供できる、いくつかの設計パターンがあります。ただしこれらの実装は、従来の非同期的な戻り値を使用する方法よりも複雑です。
サービスおよび関数間のデバッグ
イベント駆動型システムのデバッグもまた、モノリシックアプリケーションの問題を解決する方法と異なります。異なるシステムおよびサービスがイベントを受け渡しする場合、エラーが発生したときに複数のサービスの正確な状態を記録、および再現することは困難です。各サービスと関数の呼び出しにはそれぞれのログファイルがあるため、エラーの原因となった特定のイベントに何が起こったかを判断するのは、より複雑になります。
詳細については、「Challenges with distributed systems」および「Implementing Microservices on AWS」を参照してください。
結論
イベント駆動型アーキテクチャは、現代の組織で人気が高まっています。このアプローチによりLambda ベースのアプリケーションとして設計可能な、マイクロサービスの利用が促進されます。この記事ではイベント駆動型アプローチの利点と、関連するトレードオフについて説明しました。
このシリーズのPart 2 では、Lambdaベースのアプリケーションを開発するための設計原則とベストプラクティスについて説明します。
日本語翻訳はSA福井 厚が担当しました。原文はこちらです。