キューに登録されたメッセージは配信に失敗する可能性があります。 これらの失敗したメッセージは、配信不能キューに記録されます。 配信の失敗は、ネットワーク障害、削除されたキュー、完全なキュー、認証エラー、または時間内に配信できないなどの理由が原因で発生する可能性があります。
キューに登録されたメッセージは、受信側のアプリケーションがキューから適切なタイミングでメッセージを読み取らない場合、長い間キューに残る可能性があります。 この動作は、時間に依存するメッセージには適さない場合があります。 時間に依存するメッセージには、キューに登録されたバインディングに Time to Live (TTL) プロパティが設定されています。これは、メッセージの有効期限が切れるまでのキュー内の時間を示します。 期限切れのメッセージは、配信不能キューと呼ばれる特殊なキューに送信されます。 キュー クォータの超過や認証エラーなど、他の理由でメッセージを配信不能キューに配置することもできます。
一般に、アプリケーションは、配信不能キューからメッセージを読み取る補正ロジックとエラーの理由を記述します。 補正ロジックは、障害の原因によって異なります。 たとえば、認証エラーが発生した場合は、メッセージに添付されている証明書を修正し、メッセージを再送信できます。 ターゲット キューのクォータに達したために配信に失敗した場合は、クォータの問題が解決されたことを期待して配信を再試行できます。
ほとんどのキュー システムには、システム全体の配信不能キューがあり、そのシステムから失敗したすべてのメッセージが格納されます。 メッセージ キュー (MSMQ) には、トランザクション キューへの配信に失敗したメッセージを格納するトランザクション システム全体の配信不能キューと、非トランザクション キューへの配信に失敗したメッセージを格納する非トランザクション システム全体の配信不能キューの 2 つのシステム全体の配信不能キューが用意されています。 2 つのクライアントが 2 つの異なるサービスにメッセージを送信しているため、WCF の異なるキューが同じ MSMQ サービスを共有して送信している場合、システムの配信不能キューにメッセージを混在させることが可能です。 これは常に最適であるとは限りません。 場合によっては (セキュリティなど)、あるクライアントが配信不能キューから別のクライアントのメッセージを読み取る必要がない場合があります。 共有配信不能キューでは、クライアントがキューを参照して送信したメッセージを見つける必要もあります。これは、配信不能キュー内のメッセージの数に基づいて非常にコストがかかる可能性があります。 そのため、WCFNetMsmqBinding
では、Windows Vista の MsmqIntegrationBinding,
と MSMQ は、カスタム配信不能キュー (アプリケーション固有の配信不能キューとも呼ばれます) を提供します。
カスタム配信不能キューは、メッセージを送信するために同じ MSMQ サービスを共有するクライアント間で分離を提供します。
Windows Server 2003 および Windows XP では、Windows Communication Foundation (WCF) は、キューに登録されているすべてのクライアント アプリケーションに対してシステム全体の配信不能キューを提供します。 Windows Vista では、WCF はキューに登録されたクライアント アプリケーションごとに配信不能キューを提供します。
Dead-Letter キューの使用の指定
配信不能キューは、送信側アプリケーションのキュー マネージャーにあります。 有効期限が切れている、または転送または配信に失敗したメッセージが格納されます。
バインディングには、次の配信不能キュー プロパティがあります。
Dead-Letter キューからのメッセージの読み取り
配信不能キューからメッセージを読み取るアプリケーションは、次の小さな違いを除き、アプリケーション キューから読み取る WCF サービスに似ています。
システム トランザクション配信不能キューからメッセージを読み取るには、Uri (Uniform Resource Identifier) が net.msmq://localhost/system$ の形式である必要があります。DeadXact。
システム非トランザクション配信不能キューからメッセージを読み取るには、URI が net.msmq://localhost/system$ の形式である必要があります。DeadLetter。
カスタム配信不能キューからメッセージを読み取る場合、URI は form:net.msmq://localhost/private/<custom-dlq-name>custom-dlq-name がカスタム配信不能キューの名前である必要があります。
キューのアドレス指定方法の詳細については、「 サービス エンドポイントとキューのアドレス指定」を参照してください。
受信側の WCF スタックは、サービスがリッスンしているアドレスとメッセージのアドレスと一致します。 アドレスが一致する場合は、メッセージがディスパッチされます。存在しない場合、メッセージはディスパッチされません。 配信不能キュー内のメッセージは通常、配信不能キュー サービスではなくサービスにアドレス指定されるため、配信不能キューからの読み取り時に問題が発生する可能性があります。 したがって、配信不能キューから読み取るサービスは、アドレス フィルター ServiceBehavior
をインストールする必要があります。このフィルターは、アドレス先とは関係なく、キュー内のすべてのメッセージと一致するようにスタックに指示します。 具体的には、配信不能キューからメッセージを読み取るサービスに、Any パラメーターを持つServiceBehavior
を追加する必要があります。
Dead-Letter キューからの有害メッセージ処理
有害メッセージ処理は、配信不能キューで使用できます。一部の条件があります。 システム キューからサブキューを作成できないため、システム配信不能キューから読み取るときに、 ReceiveErrorHandling
を Move
に設定することはできません。 カスタム配信不能キューから読み取る場合は、サブキューを持つ可能性があるため、 Move
は有害メッセージの有効な処理であることに注意してください。
ReceiveErrorHandling
がReject
に設定されている場合、カスタム配信不能キューから読み取ると、有害メッセージはシステムの配信不能キューに格納されます。 システムの配信不能キューから読み取ると、メッセージは削除 (消去) されます。 MSMQ のシステム配信不能キューからの拒否は、メッセージを削除 (消去) します。
例
次の例は、配信不能キューを作成する方法と、それを使用して期限切れのメッセージを処理する方法を示しています。 この例は、「 方法: キューに入ったメッセージを WCF エンドポイントと交換する」の例に基づいています。 次の例は、アプリケーションごとに配信不能キューを使用する注文処理サービスにクライアント コードを記述する方法を示しています。 この例では、配信不能キューからのメッセージを処理する方法も示します。
アプリケーションごとに配信不能キューを指定するクライアントのコードを次に示します。
using System;
using System.ServiceModel.Channels;
using System.Configuration;
//using System.Messaging;
using System.ServiceModel;
using System.Transactions;
namespace Microsoft.ServiceModel.Samples
{
//The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.
//Client implementation code.
class Client
{
static void Main()
{
// Get MSMQ queue name from appsettings in configuration.
string deadLetterQueueName = ConfigurationManager.AppSettings["deadLetterQueueName"];
// Create the transacted MSMQ queue for storing dead message if necessary.
if (!System.Messaging.MessageQueue.Exists(deadLetterQueueName))
System.Messaging.MessageQueue.Create(deadLetterQueueName, true);
OrderProcessorClient client = new OrderProcessorClient("OrderProcessorEndpoint");
try
{
// Create the purchase order.
PurchaseOrder po = new PurchaseOrder();
po.CustomerId = "somecustomer.com";
po.PONumber = Guid.NewGuid().ToString();
PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
lineItem1.ProductId = "Blue Widget";
lineItem1.Quantity = 54;
lineItem1.UnitCost = 29.99F;
PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
lineItem2.ProductId = "Red Widget";
lineItem2.Quantity = 890;
lineItem2.UnitCost = 45.89F;
po.orderLineItems = new PurchaseOrderLineItem[2];
po.orderLineItems[0] = lineItem1;
po.orderLineItems[1] = lineItem2;
//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
// Make a queued call to submit the purchase order.
client.SubmitPurchaseOrder(po);
// Complete the transaction.
scope.Complete();
}
client.Close();
}
catch(TimeoutException timeout)
{
Console.WriteLine(timeout.Message);
client.Abort();
}
catch(CommunicationException conexcp)
{
Console.WriteLine(conexcp.Message);
client.Abort();
}
Console.WriteLine();
Console.WriteLine("Press <ENTER> to terminate client.");
Console.ReadLine();
}
}
}
Imports System.ServiceModel.Channels
Imports System.Configuration
'using System.Messaging;
Imports System.ServiceModel
Imports System.Transactions
Namespace Microsoft.ServiceModel.Samples
'The service contract is defined in generatedProxy.cs, generated from the service by the svcutil tool.
'Client implementation code.
Friend Class Client
Shared Sub Main()
' Get MSMQ queue name from appsettings in configuration.
Dim deadLetterQueueName As String = ConfigurationManager.AppSettings("deadLetterQueueName")
' Create the transacted MSMQ queue for storing dead message if necessary.
If (Not System.Messaging.MessageQueue.Exists(deadLetterQueueName)) Then
System.Messaging.MessageQueue.Create(deadLetterQueueName, True)
End If
Dim client As New OrderProcessorClient("OrderProcessorEndpoint")
Try
' Create the purchase order.
Dim po As New PurchaseOrder()
po.CustomerId = "somecustomer.com"
po.PONumber = Guid.NewGuid().ToString()
Dim lineItem1 As New PurchaseOrderLineItem()
lineItem1.ProductId = "Blue Widget"
lineItem1.Quantity = 54
lineItem1.UnitCost = 29.99F
Dim lineItem2 As New PurchaseOrderLineItem()
lineItem2.ProductId = "Red Widget"
lineItem2.Quantity = 890
lineItem2.UnitCost = 45.89F
po.orderLineItems = New PurchaseOrderLineItem(1) {}
po.orderLineItems(0) = lineItem1
po.orderLineItems(1) = lineItem2
'Create a transaction scope.
Using scope As New TransactionScope(TransactionScopeOption.Required)
' Make a queued call to submit the purchase order.
client.SubmitPurchaseOrder(po)
' Complete the transaction.
scope.Complete()
End Using
client.Close()
Catch timeout As TimeoutException
Console.WriteLine(timeout.Message)
client.Abort()
Catch conexcp As CommunicationException
Console.WriteLine(conexcp.Message)
client.Abort()
End Try
Console.WriteLine()
Console.WriteLine("Press <ENTER> to terminate client.")
Console.ReadLine()
End Sub
End Class
End Namespace
クライアント構成ファイルのコードを次に示します。
配信不能キューからのメッセージを処理するサービスのコードを次に示します。
using System;
using System.ServiceModel.Description;
using System.Configuration;
using System.Messaging;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Transactions;
namespace Microsoft.ServiceModel.Samples
{
// Define a service contract.
[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
[OperationContract(IsOneWay = true)]
void SubmitPurchaseOrder(PurchaseOrder po);
}
// Service class that implements the service contract.
// Added code to write output to the console window
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, AddressFilterMode = AddressFilterMode.Any)]
public class PurchaseOrderDLQService : IOrderProcessor
{
OrderProcessorClient orderProcessorService;
public PurchaseOrderDLQService()
{
orderProcessorService = new OrderProcessorClient("OrderProcessorEndpoint");
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SimpleSubmitPurchaseOrder(PurchaseOrder po)
{
Console.WriteLine($"Submitting purchase order did not succeed ");
MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;
Console.WriteLine($"Message Delivery Status: {mqProp.DeliveryStatus} ");
Console.WriteLine($"Message Delivery Failure: {mqProp.DeliveryFailure}");
Console.WriteLine();
}
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public void SubmitPurchaseOrder(PurchaseOrder po)
{
Console.WriteLine($"Submitting purchase order did not succeed ");
MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;
Console.WriteLine($"Message Delivery Status: {mqProp.DeliveryStatus} ");
Console.WriteLine($"Message Delivery Failure: {mqProp.DeliveryFailure}");
Console.WriteLine();
// Resend the message if timed out.
if (mqProp.DeliveryFailure == DeliveryFailure.ReachQueueTimeout ||
mqProp.DeliveryFailure == DeliveryFailure.ReceiveTimeout)
{
// Re-send.
Console.WriteLine("Purchase order Time To Live expired");
Console.WriteLine("Trying to resend the message");
// Reuse the same transaction used to read the message from dlq to enqueue the message to the application queue.
orderProcessorService.SubmitPurchaseOrder(po);
Console.WriteLine("Purchase order resent");
}
}
// Host the service within this EXE console application.
public static void Main()
{
// Create a ServiceHost for the PurchaseOrderDLQService type.
using (ServiceHost serviceHost = new ServiceHost(typeof(PurchaseOrderDLQService)))
{
// Open the ServiceHostBase to create listeners and start listening for messages.
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine("The dead letter service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
serviceHost.Close();
}
}
}
}
Imports System.ServiceModel.Description
Imports System.Configuration
Imports System.Messaging
Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports System.Transactions
Namespace Microsoft.ServiceModel.Samples
' Define a service contract.
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Interface IOrderProcessor
<OperationContract(IsOneWay:=True)> _
Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder)
End Interface
' Service class that implements the service contract.
' Added code to write output to the console window
<ServiceBehavior(InstanceContextMode:=InstanceContextMode.Single, ConcurrencyMode:=ConcurrencyMode.Single, AddressFilterMode:=AddressFilterMode.Any)> _
Public Class PurchaseOrderDLQService
Implements IOrderProcessor
Private orderProcessorService As OrderProcessorClient
Public Sub New()
orderProcessorService = New OrderProcessorClient("OrderProcessorEndpoint")
End Sub
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _
Public Sub SimpleSubmitPurchaseOrder(ByVal po As PurchaseOrder)
Console.WriteLine("Submitting purchase order did not succeed ", po)
Dim mqProp As MsmqMessageProperty = TryCast(OperationContext.Current.IncomingMessageProperties(MsmqMessageProperty.Name), MsmqMessageProperty)
Console.WriteLine("Message Delivery Status: {0} ", mqProp.DeliveryStatus)
Console.WriteLine("Message Delivery Failure: {0}", mqProp.DeliveryFailure)
Console.WriteLine()
End Sub
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _
Public Sub SubmitPurchaseOrder(ByVal po As PurchaseOrder) Implements IOrderProcessor.SubmitPurchaseOrder
Console.WriteLine("Submitting purchase order did not succeed ", po)
Dim mqProp As MsmqMessageProperty = TryCast(OperationContext.Current.IncomingMessageProperties(MsmqMessageProperty.Name), MsmqMessageProperty)
Console.WriteLine("Message Delivery Status: {0} ", mqProp.DeliveryStatus)
Console.WriteLine("Message Delivery Failure: {0}", mqProp.DeliveryFailure)
Console.WriteLine()
' Resend the message if timed out.
If mqProp.DeliveryFailure = DeliveryFailure.ReachQueueTimeout OrElse mqProp.DeliveryFailure = DeliveryFailure.ReceiveTimeout Then
' Re-send.
Console.WriteLine("Purchase order Time To Live expired")
Console.WriteLine("Trying to resend the message")
' Reuse the same transaction used to read the message from dlq to enqueue the message to the application queue.
orderProcessorService.SubmitPurchaseOrder(po)
Console.WriteLine("Purchase order resent")
End If
End Sub
' Host the service within this EXE console application.
Public Shared Sub Main()
' Create a ServiceHost for the PurchaseOrderDLQService type.
Using serviceHost As New ServiceHost(GetType(PurchaseOrderDLQService))
' Open the ServiceHostBase to create listeners and start listening for messages.
serviceHost.Open()
' The service can now be accessed.
Console.WriteLine("The dead letter service is ready.")
Console.WriteLine("Press <ENTER> to terminate service.")
Console.WriteLine()
Console.ReadLine()
' Close the ServiceHostBase to shutdown the service.
serviceHost.Close()
End Using
End Sub
End Class
End Namespace
配信不能キュー サービス構成ファイルのコードを次に示します。