使用服務匯流排傳訊的效能改進最佳作法

本文描述如何使用 Azure 服務匯流排來在交換代理的訊息時將效能最佳化。 本文的第一個部分說明不同提高效能的機制。 第二個部分針對在特定案例中,利用可提供最佳效能的方式,來使用服務匯流排提供指引。

在本文中,「用戶端」一詞是指任何存取服務匯流排的實體。 用戶端可以擔任傳送者或接收者的角色。 「傳送者」一詞用於將訊息傳送至服務匯流排佇列或主題的服務匯流排佇列用戶端或主題用戶端。 「接收者」一詞指的是從服務匯流排佇列或訂用帳戶接收訊息的服務匯流排佇列用戶端或訂閱用戶端。

資源規劃和考量因素

如同任何技術資源,謹慎規劃是確保 Azure 服務匯流排能夠提供您應用程式預期之效能的關鍵。 服務匯流排命名空間的正確設定或拓撲,取決於涉及您應用程式結構因素的主機,以及各服務匯流排功能的使用方式。

定價層

服務匯流排提供各種定價層。 建議您根據應用程式的需求挑選適當的定價層。

  • 標準層:適用於應用程式對節流不敏感的開發人員/測試環境或低輸送量案例。

  • 進階層:適用於具有不同輸送量需求的實際執行環境,其中延遲和輸送量必須可預測。 此外,服務匯流排進階命名空間可以自動調整,並且可啟用以容納輸送量尖峰。

注意

如果未選擇正確的層,則會有讓服務匯流排命名空間負荷過度而造成節流的風險。

節流不會造成資料遺失。 使用服務匯流排 SDK 的應用程式可以使用預設的重試原則,以確保資料最終會由服務匯流排接收。

計算進階層的輸送量

傳送至服務匯流排的資料會序列化為二進位,接著在接收者收到時還原序列化。 因此,雖然應用程式會將訊息視為不可部分完成的工作單位,但服務匯流排會以位元組 (或 MB) 來測量輸送量。

計算輸送量需求時,請考量傳送至服務匯流排 (輸入) 的資料,以及從服務匯流排接收 (輸出) 的資料。

一如預期,可一併批次處理的較小訊息承載的輸送量比較高。

效能評定

以下是您可以執行的 GitHub 範例,以查看您針對 服務匯流排 命名空間收到的預期輸送量。 在我們的基準測試中,我們觀察到每個傳訊單位 (MU) 的輸入和輸出大約是 4 MB/秒。

此基準測試範例沒有使用任何進階功能,因此您應用程式觀察到的輸送量會因案例而有所不同。

計算考量

使用特定 服務匯流排 功能需要計算使用率,才能降低預期的輸送量。 其中一些功能包括:

  1. 工作階段。
  2. 在單一主題上展開傳送給多個訂閱。
  3. 在單一訂閱上執行多個篩選條件。
  4. 排定的訊息。
  5. 延遲訊息。
  6. Transactions。
  7. 取消重複和回溯查看時間範圍。
  8. 轉送至 (從一個實體轉送到另一個實體)。

如果您的應用程式使用了上述的任何功能,而且您未收到預期的輸送量,您可以檢閱 CPU 使用計量,並考慮擴大您的服務匯流排進階命名空間。

您也可以利用 Azure 監視器來自動調整服務匯流排命名空間

跨命名空間分區化

雖然將配置給命名空間的計算(傳訊單位)相應增加是較容易的解決方案,但 可能無法 提供輸送量的線性增加。 這是因為 服務匯流排 內部(記憶體、網路等),這可能會限制輸送量。

在此情況下,更簡潔的解決方案是將您的實體 (佇列和主題) 跨不同的服務匯流排進階命名空間進行分區化。 您也可以考慮在不同的 Azure 區域中跨不同命名空間進行分區化。

通訊協定

服務匯流排可讓用戶端透過三種通訊協定的其中一種傳送和接收訊息:

  1. 進階訊息佇列通訊協定 (AMQP)
  2. 服務匯流排傳訊通訊協定 (SBMP)
  3. 超文字傳輸通訊協定 (HTTP)

AMQP 最有效率,因為它可以維護與服務匯流排的連線。 它也會實作批處理和 預先擷取。 除非明確提到,否則本文中的所有內容都假設為使用 AMQP 和 SBMP。

重要

SBMP 通訊協定僅適用於 .NET Framework。 AMQP 是 .NET Standard 的預設值。

2026 年 9 月 30 日我們將淘汰 Azure 服務匯流排的 SBMP 通訊協定支援,因此您將無法在 2026 年 9 月 30 日之後再使用此通訊協定。 請在該日期之前移轉至使用 AMQP 通訊協定的最新 Azure 服務匯流排 SDK 程式庫,該程式庫提供重要的安全性更新和改進的功能。

如需詳細資訊,請參閱支援淘汰公告

選擇適當的服務匯流排 .NET SDK

套件 Azure.Messaging.ServiceBus 是 2020 年 11 月提供的最新 Azure 服務匯流排 .NET SDK。 兩款較舊的 .NET SDK 會持續收到重大錯誤修正,直到 2026 年 9 月 30 日,但我們強烈建議您改用最新的 SDK。 如需如何從舊版 SDK 移轉的詳細資訊,請參閱移轉指南

NuGet 封裝 主要命名空間 最低平臺 通訊協定
Azure.Messaging.ServiceBus (最新) Azure.Messaging.ServiceBus
Azure.Messaging.ServiceBus.Administration
.NET Core 2.0
.NET Framework 4.6.1
Mono 5.4
通用 Windows 平台 10.0.16299
AMQP
HTTP
Microsoft.Azure.ServiceBus Microsoft.Azure.ServiceBus
Microsoft.Azure.ServiceBus.Management
.NET Core 2.0
.NET Framework 4.6.1
Mono 5.4
通用 Windows 平台 10.0.16299
AMQP
HTTP

如需最低 .NET Standard 平台支援的詳細資訊,請參閱 .NET 實作支援

在 2026 年 9 月 30 日,我們將淘汰不符合 Azure SDK 準則的 Azure 服務匯流排 SDK 程式庫 WindowsAzure.ServiceBus、Microsoft.Azure.ServiceBus 和 com.microsoft.azure.servicebus。 我們也將結束 SBMP 通訊協定的支援,因此您將無法在 2026 年 9 月 30 日之後再使用此通訊協定。 請在該日期之前移轉至最新的 Azure SDK 程式庫,該程式庫提供重要的安全性更新和改進的功能。

雖然較舊的程式庫仍可在 2026 年 9 月 30 日之後使用,但它們將不再收到 Microsoft 的官方支援和更新。 如需詳細資訊,請參閱支援淘汰公告

重複使用處理站和用戶端

與服務互動的服務匯流排用戶端,例如 ServiceBusClientServiceBusSenderServiceBusReceiverServiceBusProcessor,應該註冊為做為單一相依性插入 (或具現化一次並共用)。 ServiceBusClient 可以搭配 ServiceBusClientBuilderExtensions 註冊為以相依性插入。

建議您不要在傳送或接收每則訊息之後,關閉或處置這些用戶端。 關閉或處置實體限定的物件 (ServiceBusSender/Receiver/Processor) 會導致服務匯流排服務的連結終止。 處置 ServiceBusClient 會導致與服務匯流排服務的連線終止。

本指導不適用於 ServiceBusSessionReceiver,因為其存留期與工作階段本身相同。 對於使用 ServiceBusSessionReceiver 的應用程式,建議使用 ServiceBusClient 的單一執行個體來接受每個工作階段,這會跨越繫結至該工作階段的新 ServiceBusSessionReceiver。 應用程式完成該工作階段的處理後,便應該處置相關聯的 ServiceBusSessionReceiver

下列注意事項適用於所有 SDK:

注意

建立連線是成本高昂的作業,您可以藉由重新使用多項作業的相同處理站或用戶端物件,來避免建立連線。 您可以安全地使用這些用戶端物件,從多個執行緒進行並行的非同步作業。

並行作業

處理傳送、接收、刪除等作業,都需要一些時間。 此時間包括服務匯流排服務處理作業所花費的時間,和要求和回應的延遲時間。 若要增加每次的作業數目,就必須並行執行作業。

用戶端會透過執行非同步作業來排程並行作業。 下一個要求會在前一個要求完成之前啟動。 非同步傳送作業的程式碼片段範例如下:

var messageOne = new ServiceBusMessage(body);
var messageTwo = new ServiceBusMessage(body);

var sendFirstMessageTask =
    sender.SendMessageAsync(messageOne).ContinueWith(_ =>
    {
        Console.WriteLine("Sent message #1");
    });
var sendSecondMessageTask =
    sender.SendMessageAsync(messageTwo).ContinueWith(_ =>
    {
        Console.WriteLine("Sent message #2");
    });

await Task.WhenAll(sendFirstMessageTask, sendSecondMessageTask);
Console.WriteLine("All messages sent");

非同步接收作業的程式碼範例如下。

var client = new ServiceBusClient(connectionString);
var options = new ServiceBusProcessorOptions 
{

      AutoCompleteMessages = false,
      MaxConcurrentCalls = 20
};
await using ServiceBusProcessor processor = client.CreateProcessor(queueName,options);
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;

static Task ErrorHandler(ProcessErrorEventArgs args)
{
    Console.WriteLine(args.Exception);
    return Task.CompletedTask;
};

static async Task MessageHandler(ProcessMessageEventArgs args)
{
    Console.WriteLine("Handle message");
    await args.CompleteMessageAsync(args.Message);
}

await processor.StartProcessingAsync();

接收模式

建立佇列或訂用帳戶用戶端時,您可以指定接收模式:「查看鎖定」或「接收與刪除」。 預設接收模式是PeekLock。 以預設模式操作時,用戶端會傳送要求,以接收來自服務匯流排的訊息。 用戶端收到訊息後,它會傳送要求以完成訊息。

若要將接收模式設定為ReceiveAndDelete,這兩個步驟會結合為單一要求。 這些步驟可減少整體的作業數目並可改善整體訊息輸送量。 此效能改善的風險為遺失訊息。

服務匯流排不支援接收和刪除作業的交易。 此外,在用戶端想要延遲或讓訊息寄不出的任何案例中,都需要查看鎖定語意。

預先擷取

預先擷取 可讓佇列或訂用帳戶用戶端在收到訊息時從服務載入額外的訊息。 用戶端會將這些訊息儲存在本機快取中。 快取的大小取決於 ServiceBusReceiver.PrefetchCount 屬性。 啟用預先擷取的每個用戶端會維護自己的快取。 快取不會跨用戶端共用。 如果用戶端開始接收作業且其快取是空的,則服務會傳輸一批次的訊息。 如果用戶端開始接收作業且快取包含一則訊息,訊息會從快取中擷取。

預先擷取訊息時,服務會鎖定預先擷取的訊息。 藉由鎖定,其他接收者就無法接收預先擷取的訊息。 如果接收者無法在鎖定到期之前完成訊息,訊息就會變成可供其他接收者接收。 訊息的預先擷取副本會保留在快取中。 當取用過其快取複本的接收者嘗試完成該訊息時,會收到例外狀況。 根據預設,訊息鎖定會在 60 秒之後到期。 此值可延長為 5 分鐘。 若要避免過期訊息遭到取用,請將快取大小設為:小於用戶端在鎖定逾時間隔內,可取用的訊息數目。

使用 60 秒的預設鎖定到期時,PrefetchCount 的理想值是中心所有接收者處理速率上限的 20 倍。 例如,處理站建立三個接收者,而每個接收者每秒可以處理最多 10 則訊息。 預先擷取計數不應該超過 20 X 3 X 10 = 600。 根據預設, PrefetchCount 會設定為0,這表示不會從服務擷取任何額外的訊息。

預先擷取訊息會增加佇列或訂用帳戶的整體輸送量,因為此舉可減少訊息作業的整體數目或來回次數。 然而,第一個訊息的擷取需要較長的時間 (因為訊息大小增加)。 從快取接收預先擷取的訊息比較快速,因為用戶端已經下載這些訊息。

伺服器會在它將訊息傳送至用戶端時,檢查訊息的存留時間 (TTL) 屬性。 用戶端不會在收到訊息時檢查訊息的 TTL 屬性。 相反地,即使訊息由用戶端快取時已超過訊息的 TTL,仍然可以收到訊息。

預先擷取並不會影響可計費的傳訊作業數目,而且僅適用於服務匯流排用戶端通訊協定。 HTTP 通訊協定不支援預先擷取。 同步和非同步接收作業皆可使用預先擷取。

預先擷取和 ReceiveMessagesAsync

雖然預先擷取多個訊息的概念與批次處理訊息 (ReceiveMessagesAsync) 的語意類似,但兩者有些微的差異,在同時使用這些方法時必須謹記。

預先擷取是 ServiceBusReceiver 上的設定 (或模式),ReceiveMessagesAsync 則是一種 (具有要求-回應語意的) 作業。

同時使用這些方法時,請考慮下列情況:

  • 預先擷取應該大於或等於您預期從 ReceiveMessagesAsync 接收的訊息數目。
  • 預先擷取最多可達每秒處理訊息數目的 n/3 倍,其中 n 是預設的鎖定持續時間。

使用窮盡法 (也就是保持高預先擷取數) 會遭遇一些困難,因為這表示訊息已鎖定為僅限特定的接收者使用。 建議您嘗試預先擷取值,這些值介於稍早所述的臨界值之間,並找出適合的值。

多個佇列或主題

如果單一佇列或主題無法處理預期的訊息數量,請使用多個傳訊實體。 使用多個實體時,請針對每個實體建立專屬用戶端,而不是讓所有實體使用相同的用戶端。

更多的佇列或主題表示您在部署階段要管理更多的實體。 從可擴縮性的觀點來看,由於服務匯流排已將負載分散到內部的多個記錄,因此這實際上沒有多大差異,所以您是使用六個佇列或主題還是兩個佇列或主題並沒有什麼實質差異。

您使用的服務層級會影響效能的可預測性。 如果您選擇 標準 層,輸送量和延遲是共用多租用戶基礎結構的最佳努力。 相同叢集上的其他租使用者可能會影響您的輸送量。 如果您選擇進階,則會獲得可向您提供可預測效能的資源,而您的多個佇列或主題會從該資源集區進行處理。 如需詳細資訊,請參閱定價層

分割的命名空間

當您使用分割的進階層命名空間時,具有較低傳訊單位 (MU) 的多個分割區可讓您在具有較高 MU 的單一分割區上取得更好的效能。

案例

下列各節描述典型傳訊的案例,並簡述慣用的服務匯流排設定。 輸送量速率會分類為小型 (小於 1 則訊息/秒)、中型 (1 則訊息/秒或更多但小於 100 則訊息/秒) 和高型 (100 則訊息/秒或更多)。 用戶端數目可分類為小型 (5 個或更少)、中型 (5 個以上但小於或等於 20 個) 和大型 (超過 20 個)。

高輸送量佇列

目標:最大化單一佇列的輸送量。 傳送者和接收者的數目很少。

  • 若要增加傳送到佇列的整體速率,請使用多個訊息處理站來建立傳送者。 對每個傳送者使用非同步作業或多個執行緒。
  • 若要增加從佇列接收的整體速率,請使用多個訊息處理站來建立接收者。
  • 使用非同步作業來利用用戶端批次處理。
  • 讓批次處理的存放區存取保持啟用。 此存取會增加訊息寫入至佇列的整體速率。
  • 將預先擷取計數設為 20 乘以處理站所有接收者的最高處理速率。 此計數會減少服務匯流排用戶端通訊協定傳輸數目。

多個高輸送量佇列

目標:最大化多個佇列的整體輸送量。 個別佇列的輸送量為中或高。

若要取得跨多個佇列的最大輸送量,請使用簡述的設定以最大化單一佇列的輸送量。 此外,使用不同的處理站來建立自不同佇列傳送或接收的用戶端。

低延遲性佇列

目標:最小化佇列或主題的延遲。 傳送者和接收者的數目很少。 佇列的輸送量為小或中。

  • 停用用戶端批次處理。 用戶端會立即傳送訊息。
  • 停用批次處理的存放區存取。 服務會立即將訊息寫入至存放區中。
  • 如果使用單一用戶端,請將預先擷取計數設為 20 乘以接收者的處理速率。 如果多個訊息同時抵達佇列,服務匯流排用戶端通訊協定會同時將它們全部傳輸。 當用戶端收到下一個訊息時,該訊息已經在本機快取中。 快取應該很小。
  • 如果使用多個用戶端,請將預先擷取計數設為 0。 藉由設定該計數,第二個用戶端可以在第一個用戶端仍在處理第一個訊息時接收第二個訊息。

具有大量傳送者的佇列

目標:最大化具有大量傳送者之佇列或主題的輸送量。 每個傳送者都會以中等速率傳送訊息。 接收者的數目很少。

服務匯流排 最多可啟用與傳訊實體的 1,000 個並行連線。 在命名空間層級強制執行這項限制,且佇列、主題或訂閱的上限為每個命名空間的並行連線限制。 對佇列而言,這個數目是在傳送者和接收者之間共用的。 如果寄件者需要所有 1,000 個連線,請將佇列取代為主題和單一訂用帳戶。 主題最多可接受來自發件人的 1,000 個並行連線。 訂用帳戶接受來自接收者的額外 1,000 個並行連線。 如果需要超過 1,000 個並行傳送者,傳送者應該透過 HTTP 將訊息傳送至 服務匯流排 通訊協定。

若要最大化輸送量,請遵循下列步驟:

  • 如果每個傳送者都位於不同的處理序中,請對每個處理序僅使用單一處理站。
  • 使用非同步作業來利用用戶端批次處理。
  • 讓批次處理的存放區存取保持啟用。 此存取會增加訊息寫入至佇列或主題的整體速率。
  • 將預先擷取計數設為 20 乘以處理站所有接收者的最高處理速率。 此計數會減少服務匯流排用戶端通訊協定傳輸數目。

具有大量接收者的佇列

目標:最大化具有大量接收者之佇列或訂用帳戶的接收速率。 每個接收者皆以中等速率接收訊息。 傳送者的數目很少。

服務匯流排 最多可啟用實體的 1,000 個並行連線。 如果佇列需要超過 1,000 個接收者,請將佇列取代為主題和多個訂用帳戶。 每個訂用帳戶最多可支援 1,000 個並行連線。 此外,接收者可以透過 HTTP 通訊協定存取佇列。

若要最大化輸送量,請遵循下列指導方針:

  • 如果每個接收者都位於不同的處理序中,請對每個處理序僅使用單一處理站。
  • 接收者可以使用同步或非同步作業。 若指定個別接收者的中等接收速率,則完全要求的用戶端批次處理不會影響接收者輸送量。
  • 讓批次處理的存放區存取保持啟用。 此存取會減少實體的整體負載。 這也會增加訊息寫入至佇列或主題的整體速率。
  • 將預先擷取計數設為較小的值 (例如,PrefetchCount = 10)。 此計數可以避免接收者在其他接收者具有大量快取的訊息時閒置。

具有一些訂閱的主題

目標:最大化具有少量訂閱之主題的輸送量。 許多訂用帳戶收到一則訊息,表示所有訂用帳戶的合併接收速率高於傳送速率。 傳送者的數目很少。 每個訂用帳戶的接收者數目很少。

若要最大化輸送量,請遵循下列指導方針:

  • 若要增加傳送到主題的整體速率,請使用多個訊息處理站來建立傳送者。 對每個傳送者使用非同步作業或多個執行緒。
  • 若要增加從訂用帳戶接收的整體速率,請使用多個訊息處理站來建立接收者。 對每個接收者使用非同步作業或多個執行緒。
  • 使用非同步作業來利用用戶端批次處理。
  • 讓批次處理的存放區存取保持啟用。 此存取會增加訊息寫入至主題的整體速率。
  • 將預先擷取計數設為 20 乘以處理站所有接收者的最高處理速率。 此計數會減少服務匯流排用戶端通訊協定傳輸數目。

具有大量訂用帳戶的主題

目標:最大化具有大量訂用帳戶之主題的輸送量。 許多訂用帳戶收到一則訊息,表示所有訂用帳戶的合併接收速率高於傳送速率。 傳送者的數目很少。 每個訂用帳戶的接收者數目很少。

如果所有訊息都路由傳送至所有訂用帳戶,具有大量訂用帳戶的主題通常會顯露較低的整體輸送量。 這是因為每個訊息都被接收了許多次,而且在主題與其所有訂閱中的所有訊息,都會儲存在相同的存放區中。 這裡是假設每個訂閱的傳送者數目和接收者數目都很少。 服務匯流排支援每個主題最多 2,000 個訂用帳戶。

若要最大化輸送量,請嘗試下列步驟:

  • 使用非同步作業來利用用戶端批次處理。
  • 讓批次處理的存放區存取保持啟用。 此存取會增加訊息寫入至主題的整體速率。
  • 將預先擷取計數設定為接收訊息的預期速率 20 倍。 此計數會減少服務匯流排用戶端通訊協定傳輸數目。