Service Bus の配信不能キューの概要

Azure Service Bus キューおよびトピック サブスクリプションでは、配信不能キュー (DLQ) と呼ばれるセカンダリ サブキューが提供されます。 デッドレター キューを明示的に作成する必要はありません。また、メイン エンティティから独立して削除したり管理したりすることはできません。

この記事では、Service Bus のデッドレターキューについて説明します。 説明の多くは、GitHub の配信不能キューのサンプルに示されています。

配信不能キュー

配信不能キューの目的は、受信者に配信できないメッセージ、または処理できなかったメッセージを保持することです。 したがって、メッセージを DLQ から取り出して、検査することができます。 ユーザーが問題を修正し、メッセージを再送信できる機能を持つアプリケーションがあります。

API とプロトコルの観点では、DLQ は他のキューとほとんど同じですが、メッセージは親エンティティのデッドレター操作によってのみ送信できるという点が異なります。 また、有効期間は監視されず、DLQ のメッセージを配信不能にすることはできません。 デッドレターキューは、ピーク ロック配信、受信と削除、トランザクション操作などの通常の操作を完全にサポートしています。

DLQ は自動的にクリーンアップされません。 DLQ から明示的に取得し、配信不能メッセージを完了するまで、メッセージは DLQ に残ります。

配信不能キューへのパス

各キューおよび各サブスクリプションには、それぞれ専用のデッドレター サブキューがあります。 それに直接対処するには、次の構文を使用します (Azure CLI、REST、および Service Bus Explorer などのツールで使用されます)。

<queue path>/$deadletterqueue
<topic path>/Subscriptions/<subscription path>/$deadletterqueue

Azure.Messaging.ServiceBus .NET ライブラリを使用する場合、このパスは自分で作成しません。 代わりに、レシーバーを作成するときに ServiceBusReceiverOptions.SubQueueSubQueue.DeadLetter に設定します。 例については、デッドレター キューからメッセージを受信するを参照してください。

転送デッドレター キューへのパス

自動転送またはシナリオ経由でメッセージを転送先に転送できない場合、メッセージは転送先エンティティではなく転送エンティティの転送配信不能キュー (TDLQ) に配置されます。 メッセージを転送する各キューまたはサブスクリプションには、それぞれ専用の転送デッドレター サブキューがあり、次の構文を使用してそれを直接指定できます。

<queue path>/$Transfer/$DeadLetterQueue
<topic path>/Subscriptions/<subscription path>/$Transfer/$DeadLetterQueue

Azure.Messaging.ServiceBus .NET ライブラリを使用する場合は、ServiceBusReceiverOptions.SubQueueSubQueue.TransferDeadLetter に設定します。 例については、「転送デッドレター キューからメッセージを受信する」を参照してください。

DLQ のメッセージ数

配信不能キュー内のメッセージの数をトピック レベルで取得することは適用できません。メッセージはトピック レベルに配置されないためです。 送信者がトピックにメッセージを送信すると、メッセージはミリ秒以内にトピックのサブスクリプションに転送されるため、トピック レベルには存在しなくなります。 したがって、トピックのサブスクリプションに関連付けられている DLQ にメッセージが表示されます。 次の例では、Service Bus Explorer は、サブスクリプションの DLQ に現在 62 個のメッセージがあることを示しています: test1。

test1 サブスクリプションの dead-letter キューには 62 件のメッセージがあります。

Azure CLI コマンド az servicebus topic subscription showを使用して DLQ メッセージの数を取得することもできます。

DLQ にメッセージを移動する

Service Bus には、メッセージがメッセージング エンジン自体から DLQ にプッシュされる原因となるアクティビティがいくつかあります。 アプリケーションは明示的にメッセージを DLQ に移動することもできます。 次の 2 つのプロパティ (デッドレターの理由とデッドレターの説明) は、デッドレターメッセージに追加されます。 アプリケーションでは配信不能理由のプロパティに対して独自のコードを定義できますが、システムでは以下の値が設定されます。

デッドレターの理由 デッドレターエラーの説明
HeaderSizeExceeded このストリームのサイズの割り当てが制限を超えました。
TTLExpiredException メッセージの有効期限が切れ、配信不能になりました。 詳細については、「Time to live」セクションを参照してください。
Session ID is null セッションが有効なエンティティではセッション ID が Null のメッセージは許可されません。
MaxTransferHopCountExceeded キュー間で転送するときに許容される最大ホップ数が制限を超えました。 この値は 4 に設定されています。
MaxDeliveryCountExceeded 最大配信試行回数を超えてメッセージを処理できませんでした。 詳細については、「最大配信数」セクションを参照してください。

生存時間

キューまたはサブスクリプションでデッドレタリングを有効にすると、期限切れのすべてのメッセージがDLQに移動されます。 デッドレターの理由コードがTTLExpiredExceptionに設定されています。 遅延メッセージは、有効期限が切れても削除されたり、デッドレターキューに移動されたりすることはありません。 この動作は仕様です。

最大配信数

Service Busキューとサブスクリプションのメッセージ配信の試行回数には制限があります。 既定値は 10 です。 ピーク ロックの下でメッセージが配信されても、明示的に破棄されるか、ロックの有効期限が切れている場合は常に、メッセージの配信数が増加します。 配信回数が上限を超えると、メッセージは DLQ に移動されます。 DLQ 内のメッセージのデッドレター理由は MaxDeliveryCountExceeded に設定されています。 この動作を無効にすることはできませんが、最大配信回数を大きい数に設定することはできます。

受信機や接続を切る前にメッセージを済ませてください

ピークロックモードでは、受信メッセージのロックが受信側とその接続に紐づけられます。 メッセージを決済する前に受信機や接続を閉じると(完了、放棄、延期、またはデッドレター)、決済はサービスに届かず、ロックが切れるまでメッセージはロックされたままです。 その後、サービスはメッセージを再配達し、配信回数が増加します。 繰り返し受信しても解決されないメッセージは、最終的に理由 MaxDeliveryCountExceededでデッドレターキューに移動します。

このパターンを避けるために:

  • 受信機や ServiceBusClientを閉じる前に、各メッセージを処理してください。
  • 受信したメッセージを保留したまま、受信側または接続が閉じられた可能性がある後で完了しないでください。 サービスは10分後にアイドル接続を閉じ、ロックも解除します。
  • もしロックが失われて決済が失敗した場合は、元のメッセージで決済をやり直すのではなく、再度メッセージを受け取り処理してください。

以下の例は、受信側が await using ブロックで閉じられる前に各メッセージを完成させます。

await using (ServiceBusReceiver receiver = client.CreateReceiver(queueName))
{
    ServiceBusReceivedMessage message = await receiver.ReceiveMessageAsync();

    // Process the message, then settle it while the receiver is still open.
    await receiver.CompleteMessageAsync(message);
}

受信機を閉じた後にメッセージを決済すると例外が出てしまい、メッセージは決済されていません。

Important

鍵の喪失は必ずしもコードが原因とは限りません。 一時的なネットワーク障害、ネットワークの停止、またはサービスによって適用される 10 分間のアイドル タイムアウトによって、メッセージを確定する前に接続が切断される場合もあります。 そのような場合は、トラブルシューティングガイドの「 メッセージまたはセッションロックがロックの有効期限前に失われる 」を参照してください。

サブスクリプション ルールの処理中のエラー

フィルター評価の例外でデッドレタリングを有効にすると、サブスクリプションのSQLフィルター規則の実行中に発生したエラーは、問題のあるメッセージと共にDLQに記録されます。 このオプションは、DLQ メッセージが大量に読み込まれる可能性があるため、サブスクライバーがいないトピックに送信されるメッセージの種類がある運用環境では使用しないでください。 そのため、トピックに送信されるすべてのメッセージに少なくとも 1 つの一致するサブスクリプションがあることを確認します。

アプリケーション レベルのデッドレター処理

システム指定の配信不能処理機能に加え、アプリケーションでは DLQ を使用して許容できないメッセージを明示的に拒否できます。 許容できないメッセージには、システムの問題が原因で適切に処理できないメッセージ、形式が正しくないペイロードを保持しているメッセージ、またはメッセージ レベルのセキュリティスキームが使用されたときに認証に失敗するメッセージが含まれる場合があります。

.NETで、ServiceBusReceiver.DeadLetterMessageAsync メソッドを呼び出します。

DeadLetterReason に例外の型を、DeadLetterDescription に例外のスタック トレースを含めることをお勧めします。これにより、メッセージが配信不能になる問題の原因のトラブルシューティングが容易になるからです。 一部のメッセージは、Azure Service Busの Standard レベルのクォータ制限 256 KB を超える可能性があります。 Service Bus 名前空間を Standard レベルから Premium レベルにアップグレードしてクォータと上限を高くすることができます。

デッドレター キューからメッセージを受信する

Azure.Messaging.ServiceBus .NET ライブラリで配信不能メッセージを受信するには、受信側の作成時に ServiceBusReceiverOptions.SubQueueSubQueue.DeadLetter に設定します。 ライブラリが、デッドレター サブキューの宛先指定を代わりに処理します。

using Azure.Identity;
using Azure.Messaging.ServiceBus;

string fullyQualifiedNamespace = "<NAMESPACE-NAME>.servicebus.windows.net";
string queueName = "<QUEUE-NAME>";

// 1. Create the top-level client. Passwordless authentication is recommended.
await using var client = new ServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential());

// 2. Configure options to target the dead-letter sub-queue.
var options = new ServiceBusReceiverOptions
{
    SubQueue = SubQueue.DeadLetter
};

// 3. Create a receiver scoped to the dead-letter queue. For a subscription's
//    dead-letter queue, use: client.CreateReceiver(topicName, subscriptionName, options).
ServiceBusReceiver dlqReceiver = client.CreateReceiver(queueName, options);

// 4. Receive a dead-lettered message.
ServiceBusReceivedMessage dlqMessage = await dlqReceiver.ReceiveMessageAsync();

// 5. Inspect why the message was dead-lettered.
string reason = dlqMessage.DeadLetterReason;
string description = dlqMessage.DeadLetterErrorDescription;

// 6. Complete the message to remove it from the dead-letter queue.
await dlqReceiver.CompleteMessageAsync(dlqMessage);

SubQueue.TransferDeadLetter を使用すると、同様のパターンを転送配信不能キューにも適用できます。 完全な使用例については、「転送配信不能キューからメッセージを受信する」を参照してください。

自動転送シナリオにおけるデッドレター

メッセージは、以下の条件でデッドレターキューに送信されます。

  • メッセージが、連結されている 5 つ以上のキューまたはトピックを通過する。
  • 送信先キューまたはトピックが無効または削除されている。
  • 送信先キューまたはトピックがエンティティの最大サイズを超えている。

送信経由シナリオにおけるデッドレター

  • 宛先キューまたはトピックが無効になっている場合、メッセージはソース・キューの転送配信不能キュー (TDLQ) に送信されます。
  • 宛先キューまたはエンティティがエンティティ サイズを超えた場合、メッセージはソース キューの TDLQ に送信されます。

ソース エンティティの TransferDeadLetterMessageCount ランタイム プロパティを読み取ることで、転送配信不能キューで待機しているメッセージの数を確認できます。 詳細については、「 メッセージ数の詳細」を参照してください。

転送デッドレター キューからメッセージを受信する

転送配信不能キューはソース エンティティのサブキューであるため、通常の配信不能キューから受信するのと同じ方法で受信します。受信側をソース キュー (またはソース トピック サブスクリプション) にスコープ設定し、転送配信不能サブキューを選択します。

Azure.Messaging.ServiceBus .NET ライブラリを使用する場合は、レシーバーの作成時にServiceBusReceiverOptions.SubQueueSubQueue.TransferDeadLetterに設定します。 ライブラリが、ユーザーの代わりに転送デッドレター サブキューに対処します。

using Azure.Identity;
using Azure.Messaging.ServiceBus;

string fullyQualifiedNamespace = "<NAMESPACE-NAME>.servicebus.windows.net";

// The source queue that forwards messages. The transfer dead-letter queue
// lives on this entity, not on the destination.
string sourceQueueName = "<SOURCE-QUEUE-NAME>";

// 1. Create the top-level client. Passwordless authentication is recommended.
await using var client = new ServiceBusClient(fullyQualifiedNamespace, new DefaultAzureCredential());

// 2. Configure options to target the transfer dead-letter sub-queue.
var options = new ServiceBusReceiverOptions
{
    SubQueue = SubQueue.TransferDeadLetter
};

// 3. Create a receiver scoped to the source entity's transfer dead-letter queue.
//    For a subscription that forwards, use:
//    client.CreateReceiver(topicName, subscriptionName, options).
ServiceBusReceiver tdlqReceiver = client.CreateReceiver(sourceQueueName, options);

// 4. Receive a message that failed to transfer to its destination.
ServiceBusReceivedMessage tdlqMessage = await tdlqReceiver.ReceiveMessageAsync();

// 5. Inspect why the message couldn't be transferred.
string reason = tdlqMessage.DeadLetterReason;
string description = tdlqMessage.DeadLetterErrorDescription;

// 6. Complete the message to remove it from the transfer dead-letter queue.
await tdlqReceiver.CompleteMessageAsync(tdlqMessage);

なぜ私のメッセージはデッドレターキューに行ったのですか?

デッドレターキューで予期しないメッセージを見つけたら、各メッセージのデッドレター理由と説明を使って原因を特定しましょう。 デッドレター理由は DeadLetterReason プロパティに設定され、システムの理由は 「メッセージをDLQに移動する」に記載されています。 以下の表は、それらを特定し予防するための最も一般的な原因を示しています。

原因 識別方法 予防方法
配信回数が上限を超えました 理由は MaxDeliveryCountExceeded ハンドラーが各メッセージを確実に完了処理するようにし、処理時に発生する例外や、受信されたものの完了されないままのメッセージを調査してください。 詳細は 「最大配達回数」を参照してください。
レシーバー閉鎖後の和解 理由は MaxDeliveryCountExceededで、メッセージを完成させるとログにロックロストやレシーバークローズのエラーが表示されます。 受信機や接続を閉じる前に、各メッセージを整理してください。 受信機や接続を閉じる前に「決済メッセージ」をご覧ください。
メッセージの有効期限が切れています 理由は TTLExpiredException 生きる時間を延ばしたり、メッセージの処理を速くしたりしましょう。 「 Time to live」を参照してください。
メッセージが大きすぎる 理由は HeaderSizeExceeded メッセージサイズを減らすか、より高い上限を得るためにプレミアムティアにアップグレードしてください。
セッションIDがありません 理由は Session ID is null セッション対応エンティティに送るすべてのメッセージにセッションIDを設定してください。
自動前進ホップが多すぎる 理由は MaxTransferHopCountExceeded 自動前進チェーンの長さを4ホップ以内に短縮しましょう。 自動転送シナリオにおけるデッドレター処理を参照してください。
サブスクリプションフィルターエラー フィルター評価時の例外に対するデッドレター処理が有効になっており、フィルター ルールでエラーが発生しました。 フィルタールールを修正し、トピックに送るメッセージに対応するサブスクリプションを設定しましょう。 「 サブスクリプションルール処理中のエラー」を参照してください。

断続的なロックの喪失などの、デッドレターの理由に該当しない症状については、Service Bus トラブルシューティング ガイドを参照してください。

再処理のためにデッドレターメッセージを送信する

メッセージが配信不能になる原因となった問題を解決したら、キューまたはトピックに再送信して再処理することができます。 最も簡単な方法は、Azure ポータルで Service Bus Explorer を使用することです。これにより、配信不能キュー内のメッセージをピークしたり、必要に応じてコンテンツやプロパティを編集したり、個別またはバッチで再送信したりできます。 多くの場合、オペレーターは、失敗したメッセージの種類、ソース エンティティ、およびバッチ再送信を許可する理由を示すので、この UI を好みます。

キューまたはサブスクリプションのデッドレタリングを有効にするについての記事を参照して、メッセージの期限切れ時のデッドレタリング設定のさまざまな構成方法を学んでください。