Service Bus メッセージングを使用したパフォーマンス向上のためのベスト プラクティス

この記事では、ブローカー メッセージを交換する際のパフォーマンスを Azure Service Bus を使用して最適化する方法について説明しています。 この記事の前半では、パフォーマンスを向上させるためのさまざまなメカニズムについて説明します。 後半では、特定のシナリオで最大限のパフォーマンスを実現できるような方法で Service Bus を使用するためのガイダンスを示します。

この記事全体で、"クライアント" という用語は Service Bus にアクセスするすべてのエンティティを指します。 クライアントは送信側または受信側の役割を実行できます。 "送信側" という用語は、Service Bus キューまたはトピックにメッセージを送信する Service Bus キュー クライアントまたはトピック クライアントを指します。 "受信側" という用語は、Service Bus キューまたはサブスクリプションからメッセージを受信する Service Bus キュー クライアントまたはサブスクリプション クライアントを指します。

リソースの計画と考慮事項

技術的リソースと同様に、アプリケーションに必要なパフォーマンスを Azure Service Bus で提供するには、用意周到な計画が鍵になります。 Service Bus 名前空間の適切な構成またはトポロジは、アプリケーションのアーキテクチャや各 Service Bus 機能の使用方法など、多数の要因によって決まります。

Pricing tier

Service Bus には、さまざまな価格レベルが用意されています。 アプリケーションの要件に適したレベルを選択することをお勧めします。

  • Standard レベル - 開発およびテスト環境、またはアプリケーションがスロットリングに対してセンシティブではない低スループットのシナリオに適しています。

  • Premium レベル - 予測可能な待ち時間とスループットを必要とする、スループット要件が多様な運用環境に適しています。 また、Service Bus の premium 名前空間は自動スケーリングでき、スループットの急増に対応できます。

Note

適切なレベルが選択されていない場合、Service Bus 名前空間が過負荷になるリスクがあり、それが原因でスロットリングが発生する可能性があります。

スロットリングによってデータが失われることはありません。 Service Bus SDK を利用するアプリケーションでは、既定の再試行ポリシーを使用して、最終的にデータが Service Bus によって受け入れられるようにすることができます。

Premium のスループットの計算

Service Bus に送信されるデータはバイナリにシリアル化され、受信側が受信すると逆シリアル化されます。 このため、アプリケーションはメッセージをアトミックな作業単位と見なす一方で、Service Bus はバイト (またはメガバイト) 単位でスループットを測定します。

スループットの要件を計算するときは、Service Bus に送信されるデータ (イングレス) と、Service Bus から受信されるデータ (エグレス) について考慮してください。

当然ですが、スループットは、まとめてバッチ処理できるメッセージ ペイロードが小さければ小さいほど大きくなります。

ベンチマーク

こちらの GitHub のサンプルを実行すると、Service Bus 名前空間で発生が予想されるスループットを確認できます。 こちらのベンチマーク テストでは、イングレスとエグレスのメッセージング ユニット (MU) あたり約 4 MB/秒を観測しました。

このベンチマークのサンプルでは高度な機能は使用しないため、シナリオによってアプリケーションで観測されるスループットが異なります。

コンピューティングに関する考慮事項

特定の Service Bus 機能を使用すると、予想スループットを低下させる可能性があるコンピューティング使用率が必要になります。 これらの機能の一部を次に示します。

  1. セッション。
  2. 1 つのトピックで複数のサブスクリプションに展開する。
  3. 1 つのサブスクリプションで多数のフィルターを実行する。
  4. スケジュール設定されたメッセージ。
  5. 遅延メッセージ。
  6. トランザクション。
  7. 重複除去とルックバックの時間枠。
  8. 転送 (一方のエンティティから他方のエンティティへの転送)。

アプリケーションが上記のいずれかの機能を利用していて、予想されるスループットが得られない場合は、CPU 使用率メトリックを確認し、Service Bus Premium 名前空間のスケールアップを検討できます。

また、Azure Monitor を利用して、Service Bus 名前空間を自動的にスケーリングすることもできます。

名前空間間のシャーディング

名前空間に割り当てられたコンピューティング (メッセージング ユニット) をスケールアップすることは比較的簡単な解決策ですが、この解決策では、スループットが直線的に増加しない場合があります。 これは、Service Bus 内部 (ストレージ、ネットワークなど) によって、スループットが制限される場合があるためです。

この場合の比較的クリーンな解決策は、異なる Service Bus Premium 名前空間間でエンティティ (キューとトピック) をシャードすることです。 また、異なる Azure リージョン内の異なる名前空間間でのシャーディングも検討できます。

プロトコル

Service Bus を使用すると、クライアントは次の 3 つのプロトコルのいずれかを使用してメッセージを送受信できます。

  1. Advanced Message Queuing Protocol (AMQP)
  2. Service Bus メッセージング プロトコル (SBMP)
  3. ハイパーテキスト転送プロトコル (HTTP)

AMQP は、Service Bus への接続を維持するため、最も効率的です。 バッチとプリフェッチも実装されています。 明示的に示されていない限り、この記事のすべてのコンテンツで AMQP または SBMP を使用するものとします。

重要

SBMP プロトコルは、.NET Framework のみで使用できます。 AMQP は、.NET Standard の既定です。

2026 年 9 月 30 日に Azure Service Bus 用の SBMP プロトコルのサポートは終了するため、2026 年 9 月 30 日以降はこのプロトコルを使用できなくなります。 その日付より前に、(重要なセキュリティ更新プログラムと強化された機能が提供される) AMQP プロトコルを使った最新の Azure Service Bus SDK ライブラリに移行してください。

詳細については、サポート廃止のお知らせに関するページを参照してください。

適切な Service Bus .NET SDK の選択

Azure.Messaging.ServiceBus パッケージは、2020 年 11 月の時点で利用可能な最新の Azure Service Bus .NET SDK です。 2026 年 9 月 30 日まで重要なバグの修正プログラムが提供され続ける 2 つの古い .NET SDK が存在しますが、代わりに最新の 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 Service Bus SDK ライブラリ WindowsAzure.ServiceBus、Microsoft.Azure.ServiceBus、および com.microsoft.azure.servicebus は廃止されます。 SBMP プロトコルのサポートも終了するため、2026 年 9 月 30 日以降はこのプロトコルを使用できなくなります。 この日付より前に、重要なセキュリティ更新プログラムと強化された機能が提供される、最新の Azure SDK ライブラリに移行してください。

古いライブラリは 2026 年 9 月 30 日以降も引き続き使用できますが、Microsoft から公式のサポートと更新プログラムは提供されなくなります。 詳細については、サポート廃止のお知らせに関するページを参照してください。

ファクトリとクライアントの再利用

サービスと相互操作する Service Bus クライアント (ServiceBusClientServiceBusSenderServiceBusReceiverServiceBusProcessor など) は、依存関係の挿入のため、シングルトンとして登録する必要があります (または、一度インスタンス化して共有する必要があります)。 ServiceBusClient は、依存関係の挿入のため、ServiceBusClientBuilderExtensions に登録できます。

これらのクライアントは、各メッセージの送信または受信後に閉じたり破棄したりしないことをお勧めします。 エンティティ固有のオブジェクト (ServiceBusSender/Receiver/Processor) を閉じるたり破棄したりすると、Service Bus サービスへのリンクが解除されます。 ServiceBusClient を破棄すると、Service Bus サービスへの接続が切断されます。

このガイダンスは ServiceBusSessionReceiver には適用されません。有効期間がセッション自体と同じであるためです。 ServiceBusSessionReceiver と連携するアプリケーションでは、ServiceBusClient のシングルトン インスタンスを使用して各セッションを受け入れることをお勧めします。これには、そのセッションにバインドされた新しい ServiceBusSessionReceiver が含まれます。 アプリケーションがそのセッションの処理を完了すると、関連付けられている ServiceBusSessionReceiverを破棄する必要があります。

次の注意事項はすべての SDK に当てはまります。

Note

接続の確立は費用のかかる操作です。この操作は、同じファクトリまたはクライアント オブジェクトを複数の操作に再利用することで回避できます。 これらのクライアントオブジェクトは、同時実行の非同期操作のために、複数のスレッドから安全に使用できます。

同時実行の操作

送信、受信、削除などの操作には、時間がかかります。 この時間には、Service Bus サービスが操作を処理するための時間や、要求と応答の待機時間が含まれます。 時間あたりの操作数を増やすには、操作を同時に実行する必要があります。

クライアントは非同期操作を実行することによって、同時実行操作のスケジュールを設定します。 前の要求が完了する前に次の要求が開始されます。 次のコード スニペットは、非同期送信操作の例です。

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 です。 この規定のモードで操作するとき、クライアントは Service Bus からメッセージを受信する要求を送信します。 クライアントはメッセージを受信した後で、メッセージを完了する要求を送信します。

受信モードを ReceiveAndDelete に設定すると、1 つの要求に両方の手順が連結されます。 これらの手順によって、操作全体の数が削減され、全体的なメッセージ スループットを改善できます。 この方法でパフォーマンスを改善すると、メッセージを失うリスクが生じます。

Service Bus は "受信して削除" 操作のトランザクションをサポートしません。 また、クライアントでメッセージの遅延または配信不能が必要なシナリオではピーク/ロック セマンティクスが必要になります。

プリフェッチ

プリフェッチにより、キューまたはサブスクリプションのクライアントではメッセージの受信時にサービスから追加のメッセージを読み込むことができます。 クライアントはこれらのメッセージをローカル キャッシュに格納します。 キャッシュのサイズは、ServiceBusReceiver.PrefetchCount プロパティによって決まります。 プリフェッチが有効になっているクライアントはそれぞれ独自のキャッシュを保持します。 キャッシュはクライアント間で共有されません。 クライアントが受信操作を開始するときに、そのキャッシュが空の場合、サービスはメッセージのバッチを送信します。 クライアントが受信操作を開始するときに、そのキャッシュにメッセージが含まれている場合、キャッシュからメッセージが取得されます。

メッセージがプリフェッチされると、サービスはプリフェッチされたメッセージをロックします。 このロックにより、別の受信側はプリフェッチされたメッセージを受信できなくなります。 受信側がメッセージを完了できない状態でロックの有効期限が切れた場合、他の受信側がそのメッセージを使用できるようになります。 プリフェッチされたメッセージのコピーはキャッシュに残ります。 受信側が有効期限の切れたキャッシュのコピーを使用している場合、そのメッセージを完了しようとしたときに例外を受け取ります。 既定では、メッセージのロックは 60 秒後に期限切れになります。 この値は 5 分まで拡張できます。 期限切れのメッセージの使用を防ぐには、キャッシュ サイズを、ロックのタイムアウト間隔内にクライアントが使用できるメッセージの数より小さく設定します。

60 秒間の既定のロック有効期限を使用するとき、PrefetchCount の適切な値はファクトリの全受信者の最大処理レートの 20 倍になります。 たとえば、ファクトリが 3 つの受信側を作成すると、各受信側は 1 秒あたり最大 10 個のメッセージを処理できます。 プリフェッチ数が 20 X 3 X 10 = 600 を超えないようにしてください。 既定では、PrefetchCount は 0 に設定されます。これはサービスから追加のメッセージがフェッチされないことを意味します。

メッセージをプリフェッチすると、メッセージ操作全体の数、つまりラウンド トリップが減るため、キューまたはサブスクリプションの全体でのスループットが増えます。 ただし、最初のメッセージのフェッチには (メッセージ サイズの増加に起因して) より多くの時間がかかります。 プリフェッチ済みのメッセージはクライアントが既にダウンロードしているため、キャッシュから迅速に受信できます。

サーバーがクライアントにメッセージを送信するとき、メッセージの有効期間 (TTL) プロパティがサーバーによりチェックされます。 クライアントは、メッセージを受信するときに、メッセージの TTL プロパティをチェックしません。 代わりに、メッセージがクライアントによりキャッシュされたときにメッセージの TTL を経過している場合でも、メッセージを受信できます。

プリフェッチは課金対象のメッセージ操作数に影響を与えません。また、Service Bus クライアント プロトコルでのみ利用できます。 HTTP プロトコルはプリフェッチをサポートしません。 プリフェッチは同期受信操作と非同期受信操作の両方で使用できます。

詳細については、次の PrefetchCount プロパティを参照してください。

これらのプロパティの値は、ServiceBusReceiverOptions または ServiceBusProcessorOptions で設定できます。

プリフェッチと ReceiveMessagesAsync

複数のメッセージをまとめてプリフェッチするという概念はメッセージのバッチ処理 (ReceiveMessagesAsync) と似ていますが、いくつかの小さな違いがあり、これらの方法を一緒に使用する場合には覚えておく必要があります。

プリフェッチは ServiceBusReceiver に対する構成 (またはモード) ですが、ReceiveMessagesAsync は (要求 - 応答のセマンティクスを持つ) 操作です。

これらの方法を一緒に使用する場合には、次のケースを考慮してください。

  • プリフェッチは、ReceiveMessagesAsync から受信が予想されるメッセージ数と同じか、それよりも多くする必要があります。
  • プリフェッチは、1 秒あたりに処理されるメッセージ数の最大で n/3 倍にすることができます。n は既定のロック期間です。

どん欲な方法、つまりプリフェッチ数を高く保つことには、いくつか課題があります。メッセージが特定の受信者にロックされることになるためです。 前述のしきい値の間にあるプリフェッチ値を試し、何が適合するかを特定することをお勧めします。

複数のキューまたはトピック

1 つのキューまたはトピックでは想定されるメッセージ数を処理できない場合は、複数のメッセージング エンティティを使用します。 複数のエンティティを使用するときは、すべてのエンティティに同じクライアントを使用するのではなく、エンティティごとに専用のクライアントを作成します。

キューやトピックが多いほど、デプロイ時に管理するエンティティが増えます。 スケーラビリティの観点からは、Service Bus によって既に内部的に複数のログに負荷が分散されており、ユーザーが気付くほどの大きな違いは実際にはないため、6 つのキューまたはトピックを使っても、2 つのキューまたはトピックを使っても、それほど違いません。

使用するサービス レベルは、パフォーマンスの予測可能性に影響します。 Standard レベルを選んだ場合、スループットと待ち時間は、共有マルチテナント インフラストラクチャでのベスト エフォートになります。 同じクラスター上の他のテナントにより、スループットが影響を受ける可能性があります。 Premium を選ぶと、パフォーマンスを予測できるリソースが提供され、複数のキューまたはトピックがそのリソース プールから処理されます。 詳細については、「価格レベル」をご覧ください。

パーティション分割された名前空間

パーティション分割された Premium レベルの名前空間を使用する場合、メッセージング ユニット (MU) の値が低い複数のパーティションは高い MU の値を持つ 1 つのパーティションよりもパフォーマンスが向上します。

シナリオ

次のセクションでは、一般的なメッセージング シナリオについて説明し、好ましい Service Bus 設定について概要を説明します。 スループット レートは小 (1 メッセージ/秒未満)、中 (1 メッセージ/秒以上、100 メッセージ/秒未満)、高 (100 メッセージ/秒以上) に分類されます。 クライアントの数は小 (5 以下)、中 (5 より大きく 20 以下)、大 (20 より大きい) に分類されます。

高スループット キュー

目標: 1 つのキューのスループットを最大にします。 送信側と受信側の数は小です。

  • キューへの全体的な送信レートを上げるには、複数のメッセージ ファクトリを使用して送信側を作成します。 送信側ごとに、非同期操作または複数のスレッドを使用します。
  • キューからの全体的な受信レートを上げるには、複数のメッセージ ファクトリを使用して受信側を作成します。
  • クライアント側のバッチ処理を活用するには非同期操作を使用します。
  • バッチ処理ストア アクセスを有効なままにします。 このアクセスにより、メッセージをキューに書き込む全体的なレートが上がります。
  • プリフェッチ数をファクトリの全受信側の最大処理レートの 20 倍に設定します。 この数の設定によって、Service Bus クライアント プロトコル伝送の数が減ります。

複数の高スループット キュー

目標: 複数のキューの全体的なスループットを最大にします。 個々のキューのスループットは中または高です。

複数のキュー全体で最大のスループットを得るには、説明にある設定を使用し、1 つのキューのスループットを最大化します。 また、複数のファクトリを使用し、複数のキューと送受信するクライアントを作成します。

低待機時間のキュー

目標: キューまたはトピックの待機時間を最小限に抑えます。 送信側と受信側の数は小です。 キューのスループットは小または中です。

  • クライアント側のバッチ処理を無効にします。 クライアントはすぐにメッセージを送信します。
  • バッチ処理ストア アクセスを無効にします。 サービスはメッセージをストアに直ちに書き込みます。
  • 1 つのクライアントを使用している場合、プリフェッチ数を受信側の処理レートの 20 倍に設定します。 複数のメッセージが同時にキューに到着すると、Service Bus クライアント プロトコルはそれらすべてを同時に送信します。 クライアントが次のメッセージを受信するとき、そのメッセージは既にローカル キャッシュにあります。 キャッシュは小にします。
  • 複数のクライアントを使用している場合、プリフェッチ数を 0 に設定します。 この数を設定することで、最初のクライアントが最初のメッセージを処理している間に、2 番目のクライアントは 2 番目のメッセージを受信できます。

送信側の数が多いキュー

目標: 送信側の数が多いキューまたはトピックのスループットを最大にします。 送信側はそれぞれ中程度のレートでメッセージを送信します。 受信側の数は小です。

Service Bus によって、メッセージング エンティティに最大 1,000 件コンカレント接続できます。 この制限は名前空間レベルで適用され、キュー、トピック、またはサブスクリプションは名前空間あたりのコンカレント接続数の上限によって制限されます。 キューの場合、この数は送信側と受信側で共有されます。 1,000 件の接続すべてが送信側で必要な場合は、キューをトピックと 1 つのサブスクリプションで置き換えます。 トピックは、送信側から最大 1,000 件のコンカレント接続を受け入れます。 サブスクリプションは、受信側から追加の 1,000 件のコンカレント接続を受け入れます。 1,000 件を超える同時接続が送信側で必要な場合は、送信側は HTTP 経由で Service Bus プロトコルにメッセージを送信する必要があります。

スループットを最大化するには、これらの手順に従ってください。

  • 各送信側が異なるプロセスにある場合、プロセスごとに 1 つのファクトリのみを使用します。
  • クライアント側のバッチ処理を活用するには非同期操作を使用します。
  • バッチ処理ストア アクセスを有効なままにします。 このアクセスによって、メッセージをキューまたはトピックに書き込む全体的なレートが上がります。
  • プリフェッチ数をファクトリの全受信側の最大処理レートの 20 倍に設定します。 この数の設定によって、Service Bus クライアント プロトコル伝送の数が減ります。

受信側の数が多いキュー

目標: 受信側の数が多いキューまたはサブスクリプションの受信レートを最大にします。 各受信側は中程度のレートでメッセージを受信します。 送信側の数は小です。

Service Bus によって、エンティティに最大 1,000 件コンカレント接続できるようになります。 キューが 1,000 件を超える受信側を必要とする場合は、キューをトピックと複数のサブスクリプションで置き換えます。 各サブスクリプションは最大 1,000 件のコンカレント接続をサポートします。 または、受信側は HTTP プロトコル経由でキューにアクセスできます。

スループットを最大化するには、これらのガイドラインに従ってください。

  • 各受信側が異なるプロセスにある場合、プロセスごとに 1 つのファクトリのみを使用します。
  • 受信側は同期操作または非同期操作を使用できます。 各受信側の受信レートを中にすると、受信側のスループットは完了要求のクライアント側のバッチ処理の影響を受けません。
  • バッチ処理ストア アクセスを有効なままにします。 このアクセスによって、エンティティ全体の負荷が軽減されます。 また、メッセージをキューまたはトピックに書き込む全体的なレートが上がります。
  • プリフェッチ数を小さい値 (PrefetchCount = 10 など) に設定します。 この数の設定によって、他の受信側が大量のメッセージをキャッシュしている間に受信側がアイドル状態になることを防止できます。

いくつかのサブスクリプションが含まれるトピック

目標: いくつかのサブスクリプションが含まれるトピックのスループットを最大にします。 メッセージは多くのサブスクリプションで受信されます。これはすべてのサブスクリプションの受信レートを合わせると送信レートを超えることを意味します。 送信側の数は小です。 サブスクリプションあたりの受信側の数は小です。

スループットを最大化するには、これらのガイドラインに従ってください。

  • トピックへの全体的な送信レートを上げるには、複数のメッセージ ファクトリを使用して送信側を作成します。 送信側ごとに、非同期操作または複数のスレッドを使用します。
  • サブスクリプションからの全体的な受信レートを上げるには、複数のメッセージ ファクトリを使用して受信側を作成します。 受信側ごとに、非同期操作または複数のスレッドを使用します。
  • クライアント側のバッチ処理を活用するには非同期操作を使用します。
  • バッチ処理ストア アクセスを有効なままにします。 このアクセスによって、メッセージをトピックに書き込む全体的なレートが上がります。
  • プリフェッチ数をファクトリの全受信側の最大処理レートの 20 倍に設定します。 この数の設定によって、Service Bus クライアント プロトコル伝送の数が減ります。

サブスクリプションの数が多いトピック

目標: サブスクリプションの数が多いトピックのスループットを最大にします。 メッセージは多くのサブスクリプションで受信されます。これはすべてのサブスクリプションの受信レートを合わせると送信レートを超えることを意味します。 送信側の数は小です。 サブスクリプションあたりの受信側の数は小です。

すべてのメッセージがすべてのサブスクリプションに送信される場合、通常、多数のサブスクリプションを含むトピックの全体的なスループットは低下します。 その原因は、各メッセージが何度も受信され、トピックとそのすべてのサブスクリプション内のメッセージがすべて同じストアに保存されることにあります。 ここで、サブスクリプションあたりの送信側の数と受信側の数は少ないものとします。 Service Bus はトピックあたり最大 2,000 のサブスクリプションをサポートします。

スループットを最大化するには、次の手順を試します。

  • クライアント側のバッチ処理を活用するには非同期操作を使用します。
  • バッチ処理ストア アクセスを有効なままにします。 このアクセスによって、メッセージをトピックに書き込む全体的なレートが上がります。
  • プリフェッチ数を、メッセージが受信される予想レートの 20 倍に設定します。 この数の設定によって、Service Bus クライアント プロトコル伝送の数が減ります。