Azure FunctionsとAzure Event Hubsトリガーを使って、堅牢で信頼性の高いサーバーレスソリューションを構築する方法を学びましょう。 この記事では、チェックポイントのベストプラクティス、エラー処理、そしてイベントを失わず、イベント駆動型アプリケーションが安定かつレジリエントに保たれるようにするためのサーキットブレーカーパターンの実装について解説します。
分散システムにおけるイベント ストリームの課題
1 秒あたり 100 イベントの一定のレートでイベントを送信するシステムを考えてみましょう。 この速度では、複数の並列インスタンスが毎秒100件のイベントを消費してしまいます。
ただし、イベント ストリームを使用する場合は、次の課題を考慮してください。
- イベント発行元が破損したイベントを送信します。
- 関数コードでハンドルされない例外が発生します。
- ダウンストリーム システムがオフラインになり、イベント処理がブロックされます。
処理中にメッセージをロックする Azure キュー ストレージ トリガーとは異なり、ストリーム内の単一のポイントからパーティションごとに読み取りをAzure Event Hubsします。 この読み取り動作は、ビデオ プレーヤーに似ていますが、高スループット、複数のコンシューマー グループ、再生機能の望ましい利点を提供します。 イベントはチェックポイントから前方または後方に読み取られますが、新しいイベントを処理するにはポインターを移動する必要があります。 詳細については、Event Hubs ドキュメントの チェックポイント を参照してください。
ストリームでエラーが発生し、ポインターを進めないことを選択した場合、それ以降のイベント処理はブロックされます。 つまり、単一のイベントを処理する問題を処理するためにポインタを停止すると、未処理のイベントが蓄積され始めます。
関数は、成功または失敗に関係なく、ストリームのポインターを常に進めることでデッドロックを回避します。 ポインターは進み続けるため、関数はエラーに適切に対処する必要があります。
イベント ハブ トリガーがイベントを処理する方法
Azure Functionsは、次の手順を実行して、イベント ハブからのイベントを使用します。
- トリガーはイベントハブの各パーティションごとにAzure Storageにポインタを作成し、永続化します。
- トリガーは新しいイベントをバッチで受け取り(デフォルトで)、ホストはその関数をトリガーしようとし、処理用のバッチを供給します。
- 関数が実行を完了すると、例外の有無にかかわらず、トリガーはポインタを進め、チェックポイントをデフォルトのホストストレージアカウントに保存します。
- 条件が関数実行を妨げると、ホストはポインタを進めることができません。 ポインターを進めることができない場合は、後続の実行で同じイベントが再処理されます。
この動作により、いくつかの重要な点が明らかになります。
ハンドルされない例外により、イベントが失われる可能性があります。
例外を発生させる関数の実行では、ポインターが続行されます。 再試行ポリシーまたはその他の再試行ロジックを設定すると、再試行全体が完了するまでポインターが遅れます。
Functions では、 少なくとも 1 回の 配信が保証されます。
コードと依存システムでは、同じイベントが 2 回処理される可能性があるという事実を考慮する必要がある場合があります。 詳細については、Azure Functions の同一入力向け設計を参照してください。
Checkpointの状態はAzure Storageに格納されています:
トリガーは関数アプリの
AzureWebJobsStorage設定によって設定されたストレージアカウント内のチェックポイント(処理ポインタ)を永続させます。 この保存されたチェックポイント参照は以下の通りです:-
AzureWebJobsStorageを別のストレージアカウントを参照するように変更すると、関数は新しい位置から処理を開始し、その結果イベントが再処理されることがあります。 - イベントハブが削除されて再作成されると、イベントストリームの位置(シーケンス番号やオフセットなど)はリセットされますが、保存されたチェックポイント参照は変更されません。 この場合、チェックポイントが手動で削除されるまで新しいイベントを処理しないことがあります。
-
例外の処理
すべての関数コードには、最高レベルのコードで try/catch ブロック を含める必要があります。Event Hubs イベントを使用する関数では、 catch ブロックを持つことがさらに重要です。 こうすることで、例外が発生すると、catch ブロックはポインターが進行する前にエラーを処理します。
再試行のメカニズムとポリシー
クラウド内の多くの例外は一時的なものであるため、エラー処理の最初の手順は常に操作を再試行することです。 組み込みの再試行ポリシーを適用することも、独自の再試行ロジックを定義することもできます。
再試行ポリシー
Functions には、Event Hubs の組み込みの再試行ポリシーが提供されています。 リトライポリシーを使う場合は、新しい例外を提出し、ホストが定義されたポリシーに基づいて再度イベントを処理しようとします。 この再試行動作には、バージョン 5.x 以降の Event Hubs 拡張機能が必要です。 詳細については、「Retry policies (再試行ポリシー)」をご覧ください。
カスタム再試行ロジック
関数自体で独自の再試行ロジックを定義することもできます。 たとえば、次の規則で示すワークフローに従うポリシーを実装できます。
- イベントを 3 回処理してみてください (再試行の間に遅延が発生する可能性があります)。
- すべての再試行の最終的な結果が失敗した場合は、ストリームで処理を続行できるように、キューにイベントを追加します。
- 破損したイベントまたは未処理のイベントは、後で処理されます。
注
Polly は、C# アプリケーションの回復性と一時的な障害処理ライブラリの例です。
例外以外のエラー
一部の問題は、例外が発生せずに発生する可能性があります。 たとえば、要求がタイムアウトしたり、関数を実行しているインスタンスがクラッシュしたりするケースを考えてみましょう。 関数が例外なしで完了しない場合、オフセット ポインターは進むことはありません。 ポインターが進まない場合、失敗した実行後に実行されるインスタンスは、引き続き同じイベントを読み取ります。 この状況では、 少なくとも 1 回の 保証が提供されます。
すべてのイベントが少なくとも 1 回処理されるという保証は、一部のイベントが複数回処理される可能性があることを意味します。 関数アプリでは、この可能性を認識することが求められており、べき等性の原則に基づいて構築する必要があります。
エラー状態の処理
アプリは、イベント処理でいくつかのエラーを許容可能に処理できる場合があります。 ただし、ダウンストリーム処理で障害が発生した結果として発生する可能性がある永続的な障害状態を処理するように準備する必要もあります。 このような障害状態 (ダウンストリーム データ ストアがオフラインになっているなど) では、システムが正常な状態になるまで、関数はイベントのトリガーを停止する必要があります。
サーキット ブレーカー パターン
サーキット ブレーカー パターンを実装すると、アプリは効果的にイベント処理を一時停止し、後で問題が解決された後に再開できます。
イベント ストリーム プロセスでサーキット ブレーカーを実装するには、次の 2 つのコンポーネントが必要です。
- 回線の正常性を追跡および監視するために、すべてのインスタンスの共有状態。
-
openまたはclosedとして回線の状態を管理できるプライマリ プロセス。
実装の詳細は異なる場合がありますが、インスタンス間で状態を共有するには、ストレージ メカニズムが必要です。 状態は、Azure Storage、Redis Cache、または関数アプリ インスタンスからアクセスできるその他の永続的なサービスに格納できます。
Durable Functions と Azure Logic Apps は、ワークフローと回線の状態を管理するためのインフラストラクチャを提供します。 この記事では、Logic Apps を使用して関数の実行を一時停止および再起動し、サーキット ブレーカー パターンの実装に必要なコントロールを提供します。
インスタンス間で障害しきい値を定義する
複数のインスタンスがイベントを同時に処理している場合、回線の正常性を監視するには、永続化された共有外部状態が必要です。 その後、次のようなエラー状態を示すルールに基づいて、この永続化された状態を監視できます。
すべてのインスタンスで 30 秒間に 100 を超えるイベントエラーが発生した場合は、回線を中断して新しいイベントのトリガーを停止します。
この監視ロジックの実装の詳細は、特定のアプリのニーズによって異なりますが、一般的には次のシステムを作成する必要があります。
- 永続化されたストレージに対するエラーをログに記録します。
- 新しいエラーがログに記録されたときにローリング カウントを調べて、イベントエラーのしきい値が満たされているかどうかを判断します。
- このしきい値に達すると、回線を切断するようにシステムに指示するイベントを出力します。
Azure Logic Appsを使用した回線状態の管理
Azure Logic Appsには、さまざまなサービスや機能、ステートフルなオーケストレーションへの組み込みコネクタが含まれています。 回路の状態を管理するのは自然な選択です。 回線が中断する必要があるタイミングを検出したら、ロジック アプリを構築してこのワークフローを実装できます。
- 関数の処理を停止する Event Grid ワークフローをトリガーします。
- ワークフローを再起動するオプションを含む通知メールを送信します。
アプリ設定を使って特定の機能を無効化・再有効化する方法については、「Azure Functionsの機能を無効化する方法」をご覧ください。
メールの受信者は回線の状態を調査し、適切な場合は通知メール内のリンクを通じて回線を再開できます。 ワークフローが関数を再起動すると、最後のイベントハブチェックポイントからのイベントを処理します。
この方法を使うと、イベントを失わず、順番に処理し、必要なだけ回路を断ち切ることができます。
Event Grid トリガーの移行戦略
リージョン間または一部のプラン間で既存の関数アプリを移行する場合は、移行プロセス中にアプリを再作成する必要があります。 この場合、移行プロセス中に、同じイベントストリームから消費し、同じ出力宛先に書き込みできる2つのアプリが存在するかもしれません。
移行プロセス中のイベントデータの損失や重複を避けるために、 コンシューマーグループの利用を検討してください:
新しいターゲット アプリの新しいコンシューマー グループを作成します。
この新しいコンシューマー グループを使用するように、新しいアプリでトリガーを構成します。
この方法を用いることで、両アプリは検証中に独立してイベントを処理できます。
新しいアプリがイベントを正しく処理していることを確認します。
元のアプリを停止するか、サブスクリプションや消費者グループを削除してください。