次の方法で共有


Azure Service Bus のトラブルシューティング

この記事では、障害調査手法、コンカレンシー、Azure Service Bus Java クライアント ライブラリの資格情報の種類に関する一般的なエラー、およびこれらのエラーを解決するための軽減手順について説明します。

ログ記録を有効にして構成する

Azure SDK for Java では、アプリケーション エラーのトラブルシューティングに役立ち、解決を迅速化するために役立つ一貫したログ記録のストーリーが提供されます。 生成されたログは、ルートの問題の特定に役立つターミナル状態に達する前に、アプリケーションのフローをキャプチャします。 ログ記録のガイダンスについては、「Azure SDK for Java でのログ記録の構成」および「トラブルシューティングの概要を参照してください。

ログ記録を有効にするだけでなく、ログ レベルを VERBOSE または DEBUG に設定すると、ライブラリの状態に関する分析情報が得られます。 次のセクションでは、詳細ログが有効になっている場合の過剰なメッセージを減らすための log4j2 と logback の構成の例を示します。

Log4J 2 の構成

Log4J 2 を構成するには、次の手順に従います。

  1. 「Log4j2 に必要な依存関係」セクションの ログサンプル pom.xmlの依存関係を使用して、pom.xml に依存関係を追加します。
  2. log4j2.xmlsrc/main/resources フォルダーに追加します。

Logbackを設定する

ログバックを構成するには、次の手順に従います。

  1. のログサンプル pom.xmlの依存関係を使用して、pom.xml の [Dependencies required for logback]\(ログバックに必要な依存関係\) セクションに依存関係を追加します。
  2. logback.xmlsrc/main/resources フォルダーに追加します。

AMQP トランスポート ログを有効にする

問題を診断するのにクライアント ログを有効にするだけでは不十分な場合は、基になる AMQP ライブラリ (Qpid Proton-Jファイルへのログ記録を有効にできます。 Qpid Proton-J では java.util.loggingが使用されます。 ログ記録を有効にするには、次のセクションに示す内容を含む構成ファイルを作成します。 または、proton.trace.level=ALL と、java.util.logging.Handler 実装に必要な構成オプションを設定します。 実装クラスとそのオプションについては、Java 8 SDK ドキュメント java.util.logging をパッケージ化するを参照してください。

AMQP トランスポート フレームをトレースするには、PN_TRACE_FRM=1 環境変数を設定します。

「logging.properties」サンプル ファイル

次の構成ファイルは、Proton-J からの TRACE レベルの出力をファイル proton-trace.logに記録します。

handlers=java.util.logging.FileHandler
.level=OFF
proton.trace.level=ALL
java.util.logging.FileHandler.level=ALL
java.util.logging.FileHandler.pattern=proton-trace.log
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=[%1$tF %1$tr] %3$s %4$s: %5$s %n

ログ記録の削減

ログ記録を減らす 1 つの方法は、詳細度を変更する方法です。 もう 1 つの方法は、com.azure.messaging.servicebuscom.azure.core.amqpなどのロガー名パッケージからログを除外するフィルターを追加することです。 例については、「Log4J 2 の構成」セクション および「logback の構成」セクション の XML ファイルを参照してください。

バグを送信すると、次のパッケージのクラスからのログ メッセージが興味深いものになります。

  • com.azure.core.amqp.implementation
  • com.azure.core.amqp.implementation.handler
    • 例外は、ReceiveLinkHandleronDelivery メッセージを無視できることです。
  • com.azure.messaging.servicebus.implementation

ServiceBusProcessorClient でのコンカレンシー

ServiceBusProcessorClient を使用すると、アプリケーションはメッセージ ハンドラーへの呼び出しの数を同時に構成できます。 この構成により、複数のメッセージを並列で処理できます。 非セッション エンティティからのメッセージを使用する ServiceBusProcessorClient の場合、アプリケーションは、maxConcurrentCalls API を使用して目的のコンカレンシーを構成できます。 セッションが有効なエンティティの場合、必要なコンカレンシーは maxConcurrentSessions 倍の maxConcurrentCallsです。

アプリケーションで、構成されたコンカレンシーよりもメッセージ ハンドラーへの同時呼び出しの数が少ない場合は、スレッド プールのサイズが適切に設定されていない可能性があります。

ServiceBusProcessorClient は、Reactor グローバル boundedElastic スレッド プールのデーモン スレッドを使用してメッセージ ハンドラーを呼び出します。 このプール内の同時実行スレッドの最大数は、上限によって制限されます。 既定では、この上限は使用可能な CPU コアの 10 倍です。 ServiceBusProcessorClient がアプリケーションの目的のコンカレンシー (maxConcurrentCalls または maxConcurrentSessions 時間 maxConcurrentCalls) を効果的にサポートするには、必要なコンカレンシーよりも高い boundedElastic プールの上限値が必要です。 既定の上限をオーバーライドするには、システム プロパティを reactor.schedulers.defaultBoundedElasticSize設定します。

スレッド プールと CPU 割り当ては、ケース バイ ケースで調整する必要があります。 ただし、プールの上限をオーバーライドする場合は、開始点として、同時実行スレッドを CPU コアあたり約 20 から 30 に制限します。 ServiceBusProcessorClient インスタンスごとに必要なコンカレンシーを約 20 から 30 に上限にすることをお勧めします。 特定のユース ケースをプロファイリングして測定し、それに応じてコンカレンシーの側面を調整します。 負荷の高いシナリオでは、新しい ServiceBusClientBuilder インスタンスから各インスタンスがビルドされる複数の ServiceBusProcessorClient インスタンスを実行することを検討してください。 また、1 つのホストのダウンタイムがメッセージ処理全体に影響しないように、コンテナーや VM などの専用ホストで各 ServiceBusProcessorClient を実行することを検討してください。

CPU コアの少ないホストでプールの上限に高い値を設定すると、悪影響を及ぼす可能性があることに注意してください。 CPU リソースが不足しているか、CPU の数が少ないスレッドが多すぎるプールの兆候には、タイムアウトの頻度、ロックの損失、デッドロック、スループットの低下があります。 コンテナーで Java アプリケーションを実行している場合は、2 つ以上の vCPU コアを使用することをお勧めします。 コンテナー化された環境で Java アプリケーションを実行するときは、1 vCPU コア未満を選択することはお勧めしません。 リソース管理に関する詳細な推奨事項については、「Java アプリケーションをコンテナー化する」を参照してください。

接続共有のボトルネック

共有 ServiceBusClientBuilder インスタンスから作成されたすべてのクライアントは、Service Bus 名前空間への同じ接続を共有します。

共有接続を使用すると、1 つの接続でクライアント間で多重化操作が可能になりますが、クライアントが多数存在する場合や、クライアントが一緒に高負荷を生成する場合にも、共有がボトルネックになる可能性があります。 各接続には、I/O スレッドが関連付けられています。 接続を共有する場合、クライアントはこの共有 I/O スレッドの作業キューに作業を配置し、各クライアントの進行状況は、キューでの作業のタイムリーな完了によって異なります。 I/O スレッドは、エンキューされた作業を順次処理します。 つまり、共有接続の I/O スレッドの作業キューに未処理の作業が多く溜まると、症状は CPU 使用率が低い場合と似ていることがあります。 この条件は、コンカレンシーに関する前のセクションで説明されています。たとえば、クライアントのストール、タイムアウト、ロックの喪失、回復パスの速度低下などです。

Service Bus SDK は、接続 I/O スレッドに reactor-executor-* 名前付けパターンを使用します。 アプリケーションで共有接続のボトルネックが発生すると、I/O スレッドの CPU 使用率に反映される可能性があります。 また、ヒープ ダンプまたはライブ メモリでは、ReactorDispatcher$workQueue オブジェクトは I/O スレッドの作業キューです。 ボトルネック期間中のメモリ スナップショット内の長い作業キューは、共有 I/O スレッドが保留中の動作でオーバーロードされていることを示している可能性があります。

そのため、Service Bus エンドポイントに対するアプリケーションの負荷が、受信メッセージまたはペイロード の全体的な数に関して合理的に高い場合は、ビルドするクライアントごとに個別のビルダー インスタンスを使用する必要があります。 たとえば、エンティティ (キューまたはトピック) ごとに、新しい ServiceBusClientBuilder を作成し、そこからクライアントを構築できます。 特定のエンティティへの負荷が非常に高い場合は、そのエンティティに対して複数のクライアント インスタンスを作成するか、複数のホスト (コンテナーや VM など) でクライアントを実行して負荷分散を行うことができます。

Application Gateway カスタム エンドポイントの使用時にクライアントが停止する

カスタム エンドポイント アドレスは、Service Bus に解決可能な、または Service Bus にトラフィックをルーティングするように構成された、アプリケーションによって提供される HTTPS エンドポイント アドレスを参照します。 Azure Application Gateway を使用すると、Service Bus にトラフィックを転送する HTTPS フロントエンドを簡単に作成できます。 Service Bus に接続するためのカスタム エンドポイントとして Application Gateway フロントエンド IP アドレスを使用するように、アプリケーション用に Service Bus SDK を構成できます。

Application Gateway には、さまざまな TLS プロトコル バージョンをサポートする複数のセキュリティ ポリシーが用意されています。 TLSv1.2 を最小バージョンとして適用する定義済みのポリシーがあり、最小バージョンとして TLSv1.0 を持つ古いポリシーも存在します。 HTTPS フロントエンドには TLS ポリシーが適用されます。

現時点では、Service Bus SDK は Application Gateway フロントエンドによる特定のリモート TCP 終了を認識しません。これは、最小バージョンとして TLSv1.0 を使用します。 たとえば、フロントエンドが TCP FIN を送信した場合、プロパティが更新されたときに接続を閉じる ACK パケットは SDK で検出されないため、再接続されず、クライアントはメッセージを送受信できなくなります。 このような停止は、最小バージョンとして TLSv1.0 を使用する場合にのみ発生します。 軽減するには、Application Gateway フロントエンドの最小バージョンとして TLSv1.2 以上のセキュリティ ポリシーを使用します。

すべての Azure サービスでの TLSv1.0 と 1.1 のサポートは 2024 年 10 月 31 日に終了することがすでに発表されているため、TLSv1.2 への移行を強くお勧めします。

メッセージまたはセッション ロックが失われた

Service Bus キューまたはトピック サブスクリプションには、リソース レベルでロック期間が設定されています。 受信側クライアントがリソースからメッセージをプルすると、Service Bus ブローカーはメッセージに初期ロックを適用します。 最初のロックは、リソース レベルで設定されたロック期間に続きます。 有効期限が切れる前にメッセージ ロックが更新されない場合、Service Bus ブローカーはメッセージを解放して、他の受信者が使用できるようにします。 アプリケーションがロックの有効期限後にメッセージを完了または破棄しようとすると、API 呼び出しはエラー com.azure.messaging.servicebus.ServiceBusException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queueで失敗します。

Service Bus クライアントは、有効期限が切れる前にメッセージ ロックを継続的に更新するバックグラウンド ロック更新タスクの実行をサポートしています。 既定では、ロック更新タスクは 5 分間実行されます。 ロックの更新期間は、ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration)を使用して調整できます。 Duration.ZERO 値を渡すと、ロック更新タスクは無効になります。

次の一覧では、ロック損失エラーの原因となる可能性がある使用パターンまたはホスト環境の一部について説明します。

  • ロック更新タスクが無効になり、アプリケーションのメッセージ処理時間がリソース レベルで設定されたロック期間を超えています。

  • アプリケーションのメッセージ処理時間が、構成されたロック更新タスク期間を超えています。 ロックの更新期間が明示的に設定されていない場合、既定値は 5 分であることに注意してください。

  • アプリケーションは、プリフェッチ機能を有効にしました。プリフェッチ値を正の整数に設定するには、ServiceBusReceiverClientBuilder.prefetchCount(prefetch)を使用します。 プリフェッチ機能が有効になっている場合、クライアントは Service Bus エンティティ (キューまたはトピック) からプリフェッチと同じ数のメッセージを取得し、メモリ内プリフェッチ バッファーに格納します。 メッセージは、アプリケーションに受信されるまでプリフェッチ バッファーに保持されます。 クライアントは、プリフェッチ バッファー内にある間、メッセージのロックを拡張しません。 プリフェッチ バッファーに留まっている間にメッセージ ロックの有効期限が切れるほどアプリケーションの処理に時間がかかる場合、アプリケーションは期限切れのロックでメッセージを取得する可能性があります。 詳細については、「プリフェッチが既定のオプションではない理由」を参照してください。

  • ホスト環境には、ネットワークの一時的な障害や停止など、ロック更新タスクがロックを更新できないというネットワークの問題が時折発生します。

  • ホスト環境に十分な CPU が不足しているか、間欠的に CPU サイクルが不足しているため、ロック更新タスクの実行が期限切れになります。

  • ホスト システムの時刻が正確ではありません 。たとえば、クロックが歪んでいる場合、ロック更新タスクが遅れ、時間に従って実行されないようにします。

  • 接続 I/O スレッドがオーバーロードされ、ロック更新ネットワーク呼び出しを時間通り実行する機能に影響します。 この問題は、次の 2 つのシナリオで発生する可能性があります。

    • アプリケーションで、同じ接続を共有する受信側クライアントが多すぎます。 詳細については、「接続共有のボトルネック」セクションを参照してください。
    • アプリケーションは、大きな maxMessages または maxConcurrentCalls 値を持つ ServiceBusReceiverClient.receiveMessages または ServiceBusProcessorClient を構成しました。 詳細については、「ServiceBusProcessorClient での コンカレンシー」セクションを参照してください。

クライアントのロック更新タスクの数は、ServiceBusProcessorClient または ServiceBusReceiverClient.receiveMessagesに設定された maxMessages または maxConcurrentCalls パラメーター値と同じです。 複数のネットワーク呼び出しを行うロック更新タスクの数が多い場合は、Service Bus 名前空間の調整にも悪影響を及ぼす可能性があります。

ホストに十分なリソースが確保されていない場合、実行中のロック更新タスクが数個しかない場合でも、ロックが失われる可能性があります。 コンテナーで Java アプリケーションを実行している場合は、2 つ以上の vCPU コアを使用することをお勧めします。 コンテナー化された環境で Java アプリケーションを実行する場合は、1 vCPU コア未満を選択することはお勧めしません。 リソース管理に関する詳細な推奨事項については、「Java アプリケーションをコンテナー化する」を参照してください。

ロックに関する同じ注釈は、セッションが有効になっている Service Bus キューまたはトピック サブスクリプションにも関連します。 受信側クライアントがリソース内のセッションに接続すると、ブローカーはセッションに初期ロックを適用します。 セッションのロックを維持するには、クライアントのロック更新タスクが期限切れになる前にセッション ロックを更新し続ける必要があります。 セッションが有効なリソースの場合、基になるパーティションは、Service Bus ノード間で負荷分散を実現するために移動することがあります。たとえば、負荷を共有するために新しいノードが追加された場合などです。 その場合、セッション ロックが失われる可能性があります。 セッション ロックが失われた後にアプリケーションがメッセージを完了または破棄しようとすると、API 呼び出しはエラー com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiverで失敗します。

7.15.x または最新のバージョンにアップグレードする

問題が発生した場合は、最初に Service Bus SDK の最新バージョンにアップグレードして解決を試みる必要があります。 バージョン 7.15.x は大規模な再設計であり、長年にわたるパフォーマンスと信頼性の問題を解決します。

バージョン 7.15.x 以降では、スレッド ホッピングの削減、ロックの削除、ホット パスでのコードの最適化、メモリ割り当ての削減が行われます。 これらの変更により、ServiceBusProcessorClientで最大 45 ~ 50 倍のスループットが得られます。

バージョン 7.15.x 以降には、さまざまな信頼性の向上も伴います。 複数の競合状態 (プリフェッチやクレジット計算など) に対応し、エラー処理が改善されました。 これらの変更により、さまざまなクライアントの種類で一時的な問題が発生する場合の信頼性が向上します。

最新のクライアントの使用

これらの機能強化を備えた新しい基になるフレームワーク (バージョン 7.15.x 以降) は、V2 スタックと呼ばれます。 このリリースラインには、基になるスタックの前の世代 (バージョン 7.14.x で使用されるスタック) と新しい V2 スタックの両方が含まれています。

既定では、一部のクライアントの種類では V2 Stack が使用されますが、他の種類では V2-Stack オプトインが必要です。 クライアントのビルド時に com.azure.core.util.Configuration 値を指定することで、クライアントの種類に対して特定のスタック (V2 または前の世代) のオプトインまたはオプトアウトを実行できます。

たとえば、次の例に示すように、ServiceBusSessionReceiverClient での V2 スタック ベースのセッション受信にはオプトインが必要です。

ServiceBusSessionReceiverClient sessionReceiver = new ServiceBusClientBuilder()
    .connectionString(Config.CONNECTION_STRING)
    .configuration(new com.azure.core.util.ConfigurationBuilder()
        .putProperty("com.azure.messaging.servicebus.session.syncReceive.v2", "true") // 'false' by default, opt-in for V2-Stack.
        .build())
    .sessionReceiver()
    .queueName(Config.QUEUE_NAME)
    .buildClient();

次の表に、クライアントの種類と対応する構成名を示し、クライアントが現在、最新バージョン 7.17.0 の V2-Stack を使用するように既定で有効になっているかどうかを示します。 既定で V2-Stack 上にないクライアントの場合は、次に示した例を使用してオプトインできます。

クライアントの種類 構成名 V2-Stack は既定でオンになっていますか?
送信者と管理クライアント com.azure.messaging.servicebus.sendAndManageRules.v2 はい
非セッション プロセッサとリアクター レシーバー クライアント com.azure.messaging.servicebus.nonSession.asyncReceive.v2 はい
セッション プロセッサ レシーバー クライアント com.azure.messaging.servicebus.session.processor.asyncReceive.v2 はい
セッション・リアクター受信クライアント com.azure.messaging.servicebus.session.reactor.asyncReceive.v2 はい
非セッション同期受信側クライアント com.azure.messaging.servicebus.nonSession.syncReceive.v2 いいえ
セッション同期受信側クライアント com.azure.messaging.servicebus.session.syncReceive.v2 いいえ

com.azure.core.util.Configurationを使用する代わりに、環境変数またはシステム プロパティを使用して同じ構成名を設定することで、オプトインまたはオプトアウトを行うことができます。

次の手順

この記事のトラブルシューティング ガイダンスが、Azure SDK for Java クライアント ライブラリを使用するときに問題を解決するのに役立たない場合は、Azure SDK for Java GitHub リポジトリに 問題を報告 することをお勧めします。