この記事では、 EventProcessorClient
の種類を使用するときに発生する可能性がある一般的な問題の解決策について説明します。 Azure Event Hubs の使用時に発生する可能性がある他の一般的な問題の解決策については、「 Azure Event Hubs のトラブルシューティング」を参照してください。
イベント プロセッサを使用する場合の 412 の前提条件エラー
412 前提条件エラーは、クライアントがパーティションの所有権を取得または更新しようとしたが、所有権レコードのローカル バージョンが古い場合に発生します。 この問題は、別のプロセッサ インスタンスがパーティションの所有権を盗んだ場合に発生します。 詳細については、次のセクションを参照してください。
パーティションの所有権の変更が頻繁に発生する
EventProcessorClient
インスタンスの数が変更されると (つまり、追加または削除されます)、実行中のインスタンスは、パーティション間の負荷分散を試みます。 プロセッサの数が変更された後、数分間、パーティションは所有者を変更する必要があります。 バランスが取れた後は、パーティションの所有権が安定し、変更頻度が低くなります。 プロセッサの数が一定のときにパーティションの所有権が頻繁に変更される場合は、問題を示している可能性があります。 ログと再現コードを含む GitHub の問題を報告することをお勧めします。
パーティションの所有権は、 CheckpointStore
の所有権レコードを使用して決定されます。 負荷分散間隔ごとに、 EventProcessorClient
は次のタスクを実行します。
- 最新の所有権レコードを取得します。
- パーティション所有権の有効期限内にタイムスタンプが更新されていないレコードを特定するために、記録を確認してください。 この条件に一致するレコードのみが考慮されます。
- 未所有のパーティションがあり、
EventProcessorClient
のインスタンス間で負荷が分散されていない場合、イベント プロセッサ クライアントはパーティションの要求を試みます。 - そのパーティションへのアクティブなリンクを持つ、所有しているパーティションの所有権レコードを更新します。
次の一覧で説明するように、EventProcessorClient
を使用してEventProcessorClientBuilder
を作成するときに、負荷分散と所有権の有効期限の間隔を構成できます。
- loadBalancingUpdateInterval(Duration) メソッドは、負荷分散サイクルの実行頻度を示します。
- partitionOwnershipExpirationInterval(Duration) メソッドは、所有権レコードが更新されてから、プロセッサが未所有のパーティションを考慮するまでの最小時間を示します。
たとえば、所有権レコードが午前 9 時 30 分に更新され、 partitionOwnershipExpirationInterval
が 2 分の場合です。 負荷分散サイクルが発生し、最後の 2 分間または午前 9 時 32 分までに所有権レコードが更新されていないことに気付いた場合、パーティションは未所有と見なされます。
パーティション コンシューマーの 1 つでエラーが発生した場合、対応するコンシューマーは閉じられますが、次の負荷分散サイクルまで解放を試みることはありません。
"...エポック '0' の現在のレシーバー '<RECEIVER_NAME>' が切断されています"
エラー メッセージ全体は、次の出力のようになります。
New receiver 'nil' with higher epoch of '0' is created hence current receiver 'nil' with epoch '0'
is getting disconnected. If you are recreating the receiver, make sure a higher epoch is used.
TrackingId:<GUID>, SystemTracker:<NAMESPACE>:eventhub:<EVENT_HUB_NAME>|<CONSUMER_GROUP>,
Timestamp:2022-01-01T12:00:00}"}
このエラーは、 EventProcessorClient
インスタンスが追加または削除された後に負荷分散が発生した場合に発生します。 負荷分散は進行中のプロセスです。 コンシューマーと共に BlobCheckpointStore
を使用する場合、コンシューマーは 、最大 30 秒 (既定) ごとに、各パーティションに対する要求を持つコンシューマーを確認し、いくつかのロジックを実行して、別のコンシューマーからパーティションを "盗む" 必要があるかどうかを判断します。 パーティションの排他的所有権をアサートするために使用されるサービス メカニズムは、 エポックと呼ばれます。
ただし、インスタンスが追加または削除されていない場合は、根本的な問題に対処する必要があります。 詳細については、 パーティションの所有権の変更に関するセクション と GitHub の問題の報告に関するセクションを参照してください。
CPU 使用率が高い
CPU 使用率が高いのは、通常、インスタンスが所有するパーティションが多すぎるためです。 CPU コアごとに 3 つ以下のパーティションを使用することをお勧めします。 CPU コアごとに 1.5 個のパーティションから始めて、所有するパーティションの数を増やすことでテストすることをお勧めします。
メモリ不足とヒープサイズの調整
JVM の現在の最大ヒープがアプリケーションの実行に不十分な場合、メモリ不足 (OOM) の問題が発生する可能性があります。 アプリケーションのヒープ要件を測定することもできます。 次に、結果に基づいて、 -Xmx
JVM オプションを使用して適切な最大ヒープ メモリを設定してヒープのサイズを変更します。
ホスト (VM またはコンテナー) に対して使用可能なメモリまたは制限セットより大きい値 (コンテナーの構成で要求されたメモリなど) として、 -Xmx
を指定しないでください。 ホストが Java ヒープをサポートするのに十分なメモリを割り当てる必要があります。
次の手順では、最大 Java ヒープの値を測定する一般的な方法について説明します。
運用環境に近い環境でアプリケーションを実行します。この環境では、運用環境で予想されるピーク負荷の下でアプリケーションがイベントを送信、受信、および処理します。
アプリケーションが安定した状態になるまで待ちます。 この段階では、アプリケーションと JVM は、すべてのドメイン オブジェクト、クラス型、静的インスタンス、オブジェクト プール (TCP、DB 接続プール) などを読み込みます。
安定状態では、次のスクリーンショットで示すような、ノコギリ歯状のヒープ コレクション パターンが表示されます。
アプリケーションが安定した状態になったら、JConsole などのツールを使用して完全ガベージ コレクション (GC) を強制します。 完全 GC 後にメモリの使用量を確認します。 完全な GC の後で占有されるのが 30% のみになるようにヒープのサイズを設定する必要があります。 この値を使用して、最大ヒープ サイズを設定できます (
-Xmx
を使用)。
コンテナーを使用している場合は、JVM インスタンスのヒープ以外のニーズに対して最大 1 GB のメモリを追加するようにコンテナーのサイズを変更します。
プロセッサ クライアントが受信を停止する
プロセッサ クライアントは、多くの場合、ホスト アプリケーションで何日間も継続的に実行されます。 場合によっては、 EventProcessorClient
が 1 つ以上のパーティションを処理していないことがわかります。 通常、例外が発生した理由を判断するのに十分な情報がありません。
EventProcessorClient
の停止は、一時的なエラーからの復旧を試みている間に発生した根本的な原因 (つまり競合状態) の症状です。 必要な情報については、 GitHub の問題の提出を参照してください。
プロセッサの再起動時に受信した EventData の重複
EventProcessorClient
と Event Hubs サービスでは、少なくとも 1 回の配信が保証されます。 メタデータを追加して、重複するイベントを識別できます。 詳細については、Stack Overflow での 少なくとも 1 回の配信が Azure Event Hubs で保証されますか? を参照してください。
1 回限りの配信が必要な場合は、クライアントからの受信確認を待機する Service Bus を検討する必要があります。 メッセージング サービスの比較については、「 Azure メッセージング サービスの選択」を参照してください。
低レベルのコンシューマー クライアントが受信を停止する
EventHubConsumerAsyncClient
は Event Hubs ライブラリによって提供される低レベルのコンシューマー クライアントであり、リアクティブ アプリケーションに対する制御と柔軟性を高める必要がある上級ユーザー向けに設計されています。 このクライアントには低レベルのインターフェイスが用意されており、ユーザーは Reactor チェーン内でバックプレッシャ、スレッド、および回復を管理できます。
EventProcessorClient
とは異なり、EventHubConsumerAsyncClient
には、すべてのターミナル原因の自動回復メカニズムは含まれません。 そのため、ユーザーはターミナル イベントを処理し、適切な Reactor オペレーターを選択して復旧戦略を実装する必要があります。
EventHubConsumerAsyncClient::receiveFromPartition
メソッドは、接続で再試行できないエラーが発生したとき、または一連の接続回復が連続して失敗し、最大再試行制限を使い果たした場合に、ターミナル エラーを生成します。 低レベルの受信側は一時的なエラーから回復しようとしますが、コンシューマー クライアントのユーザーはターミナル イベントを処理することが期待されます。 継続的なイベント受信が必要な場合は、アプリケーションで Reactor チェーンを調整して、ターミナル イベントに新しいコンシューマー クライアントを作成する必要があります。
レガシから新しいクライアント ライブラリに移行する
移行ガイドには、レガシ クライアントからの移行とレガシ チェックポイントの移行に関する手順が含まれています。
次のステップ
この記事のトラブルシューティング ガイダンスが、Azure SDK for Java クライアント ライブラリを使用するときに問題を解決するのに役立たない場合は、