Utilización de las colas de mensajes no enviados para administrar los errores en la transferencia de mensajesUsing Dead-Letter Queues to Handle Message Transfer Failures

Los mensajes en cola pueden producir un error en la entrega.Queued messages can fail delivery. Estos mensajes que no se han podido entregar se graban en una cola de mensajes no enviados.These failed messages are recorded in a dead-letter queue. Los errores en la entrega pueden deberse a motivos como errores de la red, una cola eliminada, una cola completa, error de autenticación o un error para entregar a tiempo.The failed delivery can be caused by reasons such as network failures, a deleted queue, a full queue, authentication failure, or a failure to deliver on time.

Los mensajes en cola pueden permanecer en la cola durante mucho tiempo si la aplicación receptora no los lee puntualmente de la cola.Queued messages can remain in the queue for a long time if the receiving application does not read them from the queue in a timely fashion. Este comportamiento puede no ser adecuado para los mensajes dependientes del tiempo.This behavior may not be appropriate for time-sensitive messages. Los mensajes dependientes del tiempo tienen un conjunto de propiedades de período de vida (TTL) en el enlace en cola, que indica cuánto tiempo los mensajes pueden estar en la cola antes de que deban expirar.Time-sensitive messages have a Time to Live (TTL) property set in the queued binding, which indicates how long the messages can be in the queue before they must expire. Los mensajes caducados se envían a la cola especial llamada cola de mensajes no enviados.Expired messages are sent to a special queue called the dead-letter queue. Los mensajes también se pueden colocar en una cola de mensajes no enviados por otras razones, como superar una cuota de la cola o debido a un error de autenticación.Messages can also be put in a dead-letter queue for other reasons, such as exceeding a queue quota or because of authentication failure.

Generalmente, las aplicaciones escriben lógica de compensación para leer los mensajes de la cola de mensajes no enviados y de los motivos de error.Generally, applications write compensation logic to read messages from the dead-letter queue and failure reasons. La lógica de compensación depende de la causa del error.The compensation logic depends on the cause of the failure. Por ejemplo, en el caso de error de autenticación, puede corregir el certificado adjunto al mensaje y reenviar el mensaje.For example, in the case of authentication failure, you can correct the certificate attached with the message and resend the message. Si se produjo un error en la entrega porque se alcanzó la cuota de la cola de destino, puede reintentar la entrega con la esperanza de que se haya resuelto el problema de la cuota.If delivery failed because the target queue quota was reached, you can reattempt delivery in the hope that the quota problem was resolved.

La mayoría de sistemas de puesta en cola tienen una cola de mensajes no enviados para todo el sistema donde se almacenan todos los mensajes erróneos de ese sistema.Most queuing systems have a system-wide dead-letter queue where all failed messages from that system are stored. Message Queuing (MSMQ) proporciona dos colas de mensajes no enviados para todo el sistema: una cola de mensajes no enviados transaccional para todo el sistema que almacena mensajes que produjeron un error en la entrega a la cola transaccional y una cola de mensajes no enviados no transaccional para todo el sistema que almacena mensajes que produjeron un error en la entrega a la cola no transaccional.Message Queuing (MSMQ) provides two system-wide dead-letter queues: a transactional system-wide dead-letter queue that stores messages that failed delivery to the transactional queue and a non-transactional system-wide dead-letter queue that stores messages that failed delivery to the non-transactional queue. Si dos clientes están enviando mensajes a dos servicios diferentes y, por lo tanto, comparten el mismo servicio MSMQ para enviar diferentes colas en WCF, es posible tener una combinación de los mensajes en la cola de mensajes no enviados del sistema.If two clients are sending messages to two different services, and therefore different queues in WCF are sharing the same MSMQ service to send, then it is possible to have a mix of messages in the system dead-letter queue. Esto es no siempre óptimo.This is not always optimal. En muchos casos (seguridad, por ejemplo), quizás no desee que un cliente lea los mensajes de otro cliente de una cola de mensajes no enviados.In several cases (security, for example), you may not want one client to read another client's messages from a dead-letter queue. Una cola de mensajes no enviados compartida también requiere que los clientes naveguen por la cola para buscar un mensaje que enviaron, lo que puede resultar prohibitivamente caro en función del número de mensajes en la cola de mensajes no enviados.A shared dead-letter queue also requires clients to browse through the queue to find a message that they sent, which can be prohibitively expensive based on the number of messages in the dead-letter queue. Por lo tanto, en WCFNetMsmqBinding, MsmqIntegrationBinding, y MSMQ en Windows VistaWindows Vista proporcionan una cola de mensajes no enviados personalizada (contemplada también como una cola de mensajes no enviados específica de la aplicación).Therefore, in WCFNetMsmqBinding, MsmqIntegrationBinding, and MSMQ on Windows VistaWindows Vista provide a custom dead-letter queue (sometimes referred to as an application-specific dead-letter queue).

La cola de mensajes no enviados personalizada proporciona aislamiento entre los clientes que comparten el mismo servicio de MSMQ para enviar los mensajes.The custom dead-letter queue provides isolation between clients that share the same MSMQ service to send messages.

En Windows Server 2003Windows Server 2003 y Windows XPWindows XP, Windows Communication Foundation (WCF) proporciona una cola de mensajes no enviados para todo el sistema para todas las aplicaciones cliente en cola.On Windows Server 2003Windows Server 2003 and Windows XPWindows XP, Windows Communication Foundation (WCF) provides a system-wide dead-letter queue for all queued client applications. En Windows VistaWindows Vista, WCF proporciona una cola de mensajes no enviados para cada aplicación de cliente en cola.On Windows VistaWindows Vista, WCF provides a dead-letter queue for each queued client application.

Especificar el uso de la cola de mensajes no enviadosSpecifying Use of the Dead-Letter Queue

Una cola de mensajes no enviados está en el administrador de cola de la aplicación emisora.A dead-letter queue is in the queue manager of the sending application. Almacena mensajes que han expirado o que han producido un error en la transferencia o entrega.It stores messages that have expired or that have failed transfer or delivery.

El enlace tiene las propiedades de cola de mensajes no enviados siguientes:The binding has the following dead-letter queue properties:

Leer mensajes de la cola de mensajes no enviadosReading Messages from the Dead-Letter Queue

Una aplicación que lee los mensajes de una cola de mensajes no enviados es similar a un servicio WCF que se lee de una cola de aplicación, salvo por las diferencias menores siguientes:An application that reads messages out of a dead-letter queue is similar to a WCF service that reads from an application queue, except for the following minor differences:

  • Para leer mensajes de una cola de mensajes no enviados transaccional de un sistema, el identificador URI (Uniform Resource Identifier) debe tener el formato siguiente: net.msmq://localhost/system$;DeadXact.To read messages from a system transactional dead-letter queue, the Uniform Resource Identifier (URI) must be of the form: net.msmq://localhost/system$;DeadXact.

  • Para leer mensajes de una cola de mensajes no enviados no transaccional de un sistema, el URI debe tener el formato siguiente: net.msmq://localhost/system$;DeadLetter.To read messages from a system non-transactional dead-letter queue, the URI must be of the form: net.msmq://localhost/system$;DeadLetter.

  • Para leer mensajes de una cola de mensajes no enviados personalizada, el URI debe tener el formato: MSMQ://localhost/Private/<custom-dlq-name> donde custom-dlq-name es el nombre de personalizado cola de mensajes no enviados.To read messages from a custom dead-letter queue, the URI must be of the form:net.msmq://localhost/private/<custom-dlq-name> where custom-dlq-name is the name of the custom dead-letter queue.

Para obtener más información acerca de la dirección de las colas, consulte puntos de conexión de servicio y direccionamiento de la cola.For more information about how to address queues, see Service Endpoints and Queue Addressing.

La pila de WCF en el receptor coincide con direcciones que el servicio está escuchando en con la dirección del mensaje.The WCF stack on the receiver matches addresses that the service is listening on with the address on the message. Si las direcciones coinciden, se envía el mensaje; si no, no se envía el mensaje.If the addresses match, the message is dispatched; if not, the message is not dispatched. Esto puede producir problemas al leer de la cola de mensajes no enviados, porque los mensajes en la cola de mensajes no enviados normalmente se direccionan al servicio y no al servicio de la cola de mensajes no enviados.This can cause problems when reading from the dead-letter queue, because messages in the dead-letter queue are typically addressed to the service and not the dead-letter queue service. Por consiguiente, el servicio que lee de la cola de mensajes no enviados debe instalar un filtro de direcciones ServiceBehavior que indique a la pila que haga coincidir todos los mensajes de la cola independientemente del destinatario.Therefore, the service reading from the dead-letter queue must install an address filter ServiceBehavior that instructs the stack to match all messages in the queue independently of the addressee. Específicamente, debe agregar ServiceBehavior con el parámetro Any al servicio que lee los mensajes de la cola de mensajes no enviados.Specifically, you must add a ServiceBehavior with the Any parameter to the service reading messages from the dead-letter queue.

Control de mensajes dudosos de la cola de mensajes no enviadosPoison Message Handling from the Dead-Letter Queue

El control de mensajes dudosos está disponible en las colas de mensajes no enviados, con algunas condiciones.Poison message handling is available on dead-letter queues, with some conditions. Dado que no puede crear subcolas a partir de las colas del sistema, al leer de la cola de mensajes no enviados del sistema, ReceiveErrorHandling no puede estar establecido en Move.Because you cannot create sub-queues from system queues, when reading from the system dead-letter queue, the ReceiveErrorHandling cannot be set to Move. Tenga en cuenta que si está leyendo de una cola de mensajes no enviados personalizada, puede tener subcolas y, por consiguiente, Move es una disposición válida para el mensaje dudoso.Note that if you are reading from a custom dead-letter queue, you can have sub-queues and, therefore, Move is a valid disposition for the poison message.

Cuando ReceiveErrorHandling está establecido en Reject, al leer de la cola de mensajes no enviados personalizada, el mensaje dudoso se coloca en la cola de mensajes no enviados del sistema.When ReceiveErrorHandling is set to Reject, when reading from the custom dead letter queue, the poison message is put in the system dead-letter queue. Si se está leyendo de la cola de mensajes no enviados del sistema, el mensaje se quita (se purga).If reading from the system dead-letter queue, the message is dropped (purged). Un rechazo de una cola de mensajes no enviados del sistema en MSMQ quita (purga) el mensaje.A reject from a system dead-letter queue in MSMQ drops (purges) the message.

EjemploExample

El ejemplo siguiente muestra cómo crear una cola de mensajes no enviados y cómo utilizarla para procesar los mensajes caducados.The following example shows how to create a dead-letter queue and how to use it to process expired messages. En el ejemplo se basa en el ejemplo de Cómo: Intercambiar mensajes con puntos de conexión WCF en cola.The example is based on the example in How to: Exchange Queued Messages with WCF Endpoints. El ejemplo siguiente muestra cómo escribir el código de cliente en el servicio de procesamiento de orden que utiliza una cola de mensajes no enviados para cada aplicación.The following example shows how to write the client code to the order processing service that uses a dead-letter queue for each application. El ejemplo también muestra cómo procesar los mensajes de la cola de mensajes no enviados.The example also shows how to process messages from the dead-letter queue.

A continuación, se muestra el código para un cliente que especifica una cola de mensajes no enviados para cada aplicación.The following is code for a client that specifies a dead-letter queue for each application.

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

El código siguiente muestra el archivo de configuración del cliente.The following is code for the client configuration file.

A continuación, se muestra el código para un servicio que procesa los mensajes de una cola de mensajes no enviados.The following is code for a service processing messages from a dead-letter queue.

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 ", po);
            MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;

            Console.WriteLine("Message Delivery Status: {0} ", mqProp.DeliveryStatus);
            Console.WriteLine("Message Delivery Failure: {0}", mqProp.DeliveryFailure);
            Console.WriteLine();
        }

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public void SubmitPurchaseOrder(PurchaseOrder po)
        {
            Console.WriteLine("Submitting purchase order did not succeed ", po);
            MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as 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 ||
                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

A continuación, se muestra el código para el archivo de configuración del servicio de cola de mensajes no enviados.The following is code for the dead-letter queue service configuration file.

Vea tambiénSee also