共用方式為


針對 Azure 服務匯流排 進行疑難解答

本文涵蓋 Azure 服務匯流排 Java 用戶端連結庫中認證類型的失敗調查技術、並行存取、常見錯誤,以及解決這些錯誤的風險降低步驟。

啟用和設定記錄

適用於 Java 的 Azure SDK 提供一致的記錄案例,可協助針對應用程式錯誤進行疑難解答,並協助加速解決。 產生的記錄會在到達終端狀態之前,先擷取應用程式流程來協助尋找根本問題。 如需記錄的指引,請參閱 在適用於 Java 的 Azure SDK 中設定記錄和 疑難解答概觀

除了啟用記錄之外,將記錄層級設定為 VERBOSEDEBUG 提供連結庫狀態的深入解析。 下列各節顯示範例log4j2和logback組態,以在啟用詳細資訊記錄時減少過多的訊息。

設定 Log4J 2

使用下列步驟來設定Log4J 2:

  1. 在 [Log4j2 所需的相依性] 區段中,使用記錄範例pom.xml中的相依性,在pom.xml中新增相依性
  2. 將log4j2.xml新增src/main/resources 資料夾。

設定記錄備份

使用下列步驟來設定記錄:

  1. 使用記錄範例pom.xml中的相依性,在 [記錄備份所需的相依性] 區段中,使用pom.xml中的相依性來新增相依性。
  2. 將logback.xml新增src/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

減少記錄

減少記錄的其中一種方式是變更詳細資訊。 另一種方式是新增篩選,以排除記錄檔名稱套件,例如 com.azure.messaging.servicebuscom.azure.core.amqp。 如需範例,請參閱設定Log4J 2設定記錄檔小節中的 XML 檔案。

當您提交 Bug 時,下列套件中類別的記錄訊息很有趣:

  • com.azure.core.amqp.implementation
  • com.azure.core.amqp.implementation.handler
    • 例外狀況是您可以忽略 onDelivery 中的 ReceiveLinkHandler訊息。
  • com.azure.messaging.servicebus.implementation

ServiceBusProcessorClient 中的並行存取

ServiceBusProcessorClient 可讓應用程式設定應該同時對訊息處理程式進行多少次呼叫。 此設定可讓您平行處理多個訊息。 ServiceBusProcessorClient對於來自非會話實體的取用maxConcurrentCalls訊息,應用程式可以使用 API 來設定所需的並行存取。 針對已啟用會話的實體,所需的並行存取是 maxConcurrentSessions 時間 maxConcurrentCalls

如果應用程式觀察到對訊息處理程式的並行呼叫比已設定的並行次數少,可能是因為線程集區未適當重設大小。

ServiceBusProcessorClient 會使用來自 Reactor 全域 界限Elastic 線程集區的精靈線程來叫用訊息處理程式。 此集區中並行線程的數目上限受限於上限。 根據預設,此上限是可用 CPU 核心數的十倍。 ServiceBusProcessorClient若要有效支援應用程式所需的並行存取(maxConcurrentCallsmaxConcurrentSessions時間maxConcurrentCalls),您必須具有boundedElastic高於所需並行的集區上限值。 您可以藉由設定系統屬性 reactor.schedulers.defaultBoundedElasticSize來覆寫預設上限。

您應該依案例調整線程集區和CPU配置。 不過,當您覆寫集區上限作為起點時,請將並行線程限制為每個 CPU 核心大約 20-30。 我們建議您將每個 ServiceBusProcessorClient 實例所需的並行存取上限為大約 20-30。 分析及測量您的特定使用案例,並據以調整並行層面。 針對高負載案例,請考慮執行多個 ServiceBusProcessorClient 實例,其中每個實例都是從新的 ServiceBusClientBuilder 實例建置。 此外,請考慮在專用主機中執行每個 ServiceBusProcessorClient ,例如容器或 VM,讓一部主機中的停機時間不會影響整體訊息處理。

請記住,在具有少數 CPU 核心的主機上設定集區上限的高值會產生負面影響。 CPU 資源不足或CPU數目過少的集區有一些跡象:經常逾時、鎖定遺失、死結或輸送量較低。 如果您要在容器上執行 Java 應用程式,建議您使用兩個以上的 vCPU 核心。 在容器化環境中執行 Java 應用程式時,不建議選取小於 1 個 vCPU 核心的任何專案。 如需資源方面的深入建議,請參閱 將 Java 應用程式容器化。

線上共用瓶頸

從共享ServiceBusClientBuilder實例建立的所有客戶端都會共用與 服務匯流排 命名空間相同的連線。

使用共享連線可在一個連線上的用戶端之間進行多任務處理作業,但如果有許多用戶端,或用戶端一起產生高負載,共用也可能成為瓶頸。 每個連線都有與其相關聯的 I/O 線程。 共享連線時,用戶端會將工作放入此共用 I/O 線程的工作佇列中,而每個客戶端的進度取決於其工作在佇列中的及時完成。 I/O 線程會以序列方式處理加入佇列的工作。 也就是說,如果共用連線的 I/O 線程工作佇列最終會產生許多待處理的工作,則徵兆類似於低 CPU 的佇列。 此條件會在上一節中說明並行存取 - 例如,用戶端停滯、逾時、遺失鎖定或復原路徑變慢。

服務匯流排 SDK 會reactor-executor-*使用連線 I/O 線程的命名模式。 當應用程式遇到共享連線瓶頸時,它可能會反映在 I/O 線程的 CPU 使用量中。 此外,在堆積傾印或即時記憶體中,對像是 ReactorDispatcher$workQueue I/O 線程的工作佇列。 瓶頸期間記憶體快照集內長時間的工作佇列可能表示共用 I/O 線程已多載暫止工作。

因此,如果應用程式載入至 服務匯流排 端點在傳送接收訊息或承載大小的整體數目方面相當高,您應該針對您所建置的每個用戶端使用不同的產生器實例。 例如,針對每個實體 - 佇列或主題 - 您可以建立新的 ServiceBusClientBuilder ,並從中建置用戶端。 如果對特定實體的負載極高,您可能想要為該實體建立多個用戶端實例,或在多個主機中執行用戶端,例如容器或 VM,以進行負載平衡。

用戶端在使用自定義端點時停止 應用程式閘道

自訂端點位址是指可解析為 服務匯流排 或設定為將流量路由傳送至 服務匯流排 的應用程式提供的 HTTPS 端點位址。 Azure 應用程式閘道 可讓您輕鬆地建立 HTTPS 前端,將流量轉送至 服務匯流排。 您可以設定 服務匯流排 SDK,讓應用程式使用 應用程式閘道 前端 IP 位址作為自定義端點,以連線到 服務匯流排。

應用程式閘道 提供數個支援不同 TLS 通訊協定版本的安全策略。 有預先定義的原則強制執行 TLSv1.2 作為最低版本,也有舊原則,TLSv1.0 作為最低版本。 HTTPS 前端會套用 TLS 原則。

現在,服務匯流排 SDK 無法辨識 應用程式閘道 前端的特定遠端 TCP 終止,而前端會使用 TLSv1.0 作為最低版本。 例如,如果前端傳送 TCP FIN,ACK 封包會在更新其屬性時關閉連線,SDK 就無法偵測到它,因此它不會重新連線,而且客戶端無法再傳送或接收訊息。 只有在使用 TLSv1.0 作為最低版本時,才會發生這樣的停止。 若要減輕問題,請使用具有 TLSv1.2 或更高版本的安全策略作為前端 應用程式閘道 的最低版本。

所有 Azure 服務的 TLSv1.0 和 1.1 支援已 2024 年 10 月 31 日終止,因此強烈建議您將轉換至 TLSv1.2。

訊息或會話鎖定遺失

服務匯流排 佇列或主題訂用帳戶在資源層級設定鎖定持續時間。 當接收者用戶端從資源提取訊息時,服務匯流排 訊息代理程式會將初始鎖定套用至訊息。 初始鎖定會持續於資源層級設定的鎖定持續時間。 如果訊息鎖定在到期之前未更新,則 服務匯流排 訊息代理程式會釋出訊息,使其可供其他接收者使用。 如果應用程式在鎖定到期後嘗試完成或放棄訊息,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

服務匯流排 用戶端支援執行背景鎖定更新工作,在訊息鎖定到期之前,每次更新訊息鎖定。 根據預設,鎖定更新工作會執行 5 分鐘。 您可以使用 來調整鎖定更新持續時間 ServiceBusReceiverClientBuilder.maxAutoLockRenewDuration(Duration)。 如果您傳遞 Duration.ZERO 值,則會停用鎖定更新工作。

下列清單描述一些可能導致鎖定遺失錯誤的使用模式或主機環境:

  • 鎖定更新工作已停用,且應用程式的訊息處理時間超過資源層級設定的鎖定持續時間。

  • 應用程式的訊息處理時間超過設定的鎖定更新工作持續時間。 請注意,如果未明確設定鎖定更新持續時間,則預設為 5 分鐘。

  • 應用程式已使用 ServiceBusReceiverClientBuilder.prefetchCount(prefetch)將預先擷取值設定為正整數,以開啟預先擷取功能。 啟用預先擷取功能時,用戶端會擷取等於從 服務匯流排 實體預先擷取的訊息數目 - 佇列或主題 - 並將其儲存在記憶體內部預先擷取緩衝區中。 訊息會保留在預先擷取緩衝區中,直到它們接收到應用程式為止。 用戶端不會在預先擷取緩衝區中擴充訊息的鎖定。 如果應用程式處理需要這麼長的時間,訊息鎖定會在保留預先擷取緩衝區時過期,則應用程式可能會取得具有過期鎖定的訊息。 如需詳細資訊,請參閱 為什麼預先擷取不是預設選項?

  • 主機環境偶爾會有網路問題,例如暫時性網路失敗或中斷,導致鎖定更新工作無法及時更新鎖定。

  • 主機環境缺少足夠的CPU,或間歇性地缺少CPU週期,導致鎖定更新工作無法準時執行。

  • 主機系統時間不正確,例如時鐘扭曲-延遲鎖定更新工作,並使其無法準時執行。

  • 連線 I/O 線程已多載,因而影響其實時執行鎖定更新網路呼叫的能力。 下列兩個案例可能會導致此問題:

    • 應用程式正在執行太多共用相同連線的接收者用戶端。 如需詳細資訊,請參閱 連線共用瓶頸 一節。
    • 應用程式已設定 ServiceBusReceiverClient.receiveMessagesServiceBusProcessorClient 具有大型 maxMessagesmaxConcurrentCalls 值。 如需詳細資訊,請參閱 ServiceBusProcessorClient 中的並行存取一節。

用戶端中的鎖定更新工作數目等於 maxMessages 或 所ServiceBusProcessorClient設定的 或 maxConcurrentCalls ServiceBusReceiverClient.receiveMessages參數值。 進行多個網路呼叫的大量鎖定更新工作,在命名空間節流 服務匯流排 也可能產生負面影響。

如果主機資源不足,即使只有少數鎖定更新工作正在執行,鎖定仍可能會遺失。 如果您要在容器上執行 Java 應用程式,建議您使用兩個以上的 vCPU 核心。 在容器化環境中執行 Java 應用程式時,不建議選取小於 1 個 vCPU 核心的任何專案。 如需資源方面的深入建議,請參閱 將 Java 應用程式容器化。

關於鎖定的相同備註也與已啟用會話的 服務匯流排 佇列或主題訂用帳戶相關。 當接收者用戶端連線到資源中的會話時,訊息代理程式會將初始鎖定套用至會話。 若要維護會話的鎖定,用戶端中的鎖定更新工作必須保留會話鎖定,才能到期。 針對已啟用會話的資源,基礎分割區有時會移動以跨 服務匯流排 節點達成負載平衡,例如,新增節點以共用負載。 發生這種情況時,會話鎖定可能會遺失。 如果應用程式在工作階段鎖定遺失後嘗試完成或放棄訊息,API 呼叫就會失敗,並出現錯誤 com.azure.messaging.servicebus.ServiceBusException: The session lock was lost. Request a new session receiver

升級至 7.15.x 或最新版

如果您遇到任何問題,您應該先嘗試藉由升級至最新版本的 服務匯流排 SDK 來解決這些問題。 7.15.x 版是一項重大重新設計,可解決長期效能和可靠性問題。

7.15.x 版和更新版本可減少線程跳動、移除鎖定、優化經常性路徑中的程序代碼,並減少記憶體配置。 這些變更會導致的 ServiceBusProcessorClient輸送量增加 45-50 倍。

7.15.x 版和更新版本也隨附各種可靠性改善。 它解決了數個競爭條件(例如預先擷取和信用計算),並改善了錯誤處理。 這些變更會導致在各種客戶端類型之間發生暫時性問題時,產生更佳的可靠性。

使用最新的用戶端

這些改進功能的新基礎架構在 7.15.x 版和更新版本中稱為 V2-Stack。 此發行行同時包含舊一代的基礎堆疊 -7.14.x 版所使用的堆棧,以及新的 V2-Stack。

根據預設,某些客戶端類型會使用 V2-Stack,而其他類型則需要 V2-Stack 加入加入。 您可以藉由在建置用戶端時提供 com.azure.core.util.Configuration 值,來完成特定堆疊的加入或退出特定堆疊 (V2 或上一代)。

例如,以 V2 Stack 為基礎的工作階段接收 ServiceBusSessionReceiverClient 需要加入,如下列範例所示:

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 堆疊上?
寄件人和管理用戶端 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的替代方案,您可以使用環境變數或系統屬性來設定相同的組態名稱,以執行選擇加入或退出。

下一步

如果本文中的疑難解答指引無法協助您在使用適用於 Java 的 Azure SDK 用戶端連結庫時解決問題,建議您適用於 Java 的 Azure SDK GitHub 存放庫中提出問題。