Dela via


Använda köer med obeställbara meddelanden för att hantera fel vid meddelandeöverföring

Köade meddelanden kan misslyckas med leveransen. Dessa misslyckade meddelanden registreras i en kö med obeställbara meddelanden. Den misslyckade leveransen kan orsakas av orsaker som nätverksfel, en borttagen kö, en fullständig kö, autentiseringsfel eller ett fel vid leverans i tid.

Köade meddelanden kan finnas kvar i kön under en längre tid om det mottagande programmet inte läser dem från kön i tid. Det här beteendet kanske inte är lämpligt för tidskänsliga meddelanden. Tidskänsliga meddelanden har en TTL-egenskap (Time to Live) som anges i den köade bindningen, vilket anger hur länge meddelandena kan finnas i kön innan de måste upphöra att gälla. Utgångna meddelanden skickas till en särskild kö som kallas kön med obeställbara meddelanden. Meddelanden kan också placeras i en kö med obeställbara meddelanden av andra skäl, till exempel överskrida en kökvot eller på grund av autentiseringsfel.

I allmänhet skriver program kompensationslogik för att läsa meddelanden från kön med obeställbara meddelanden och felorsaker. Kompensationslogik beror på orsaken till felet. I händelse av autentiseringsfel kan du till exempel korrigera certifikatet som är kopplat till meddelandet och skicka meddelandet igen. Om leveransen misslyckades på grund av att målkökvoten uppnåddes kan du försöka leverera igen i hopp om att kvotproblemet har lösts.

De flesta kösystem har en systemomfattande kö med obeställbara meddelanden där alla misslyckade meddelanden från systemet lagras. Message Queuing (MSMQ) tillhandahåller två systemomfattande köer med obeställbara meddelanden: en transaktionell systemomfattande kö med obeställbara meddelanden som lagrar meddelanden som inte levererades till transaktionskö och en icke-transaktionell systemomfattande kö med obeställbara meddelanden som lagrar meddelanden som inte levererades till den icke-transaktionella kön. Om två klienter skickar meddelanden till två olika tjänster och därför delar olika köer i WCF samma MSMQ-tjänst som ska skickas, är det möjligt att ha en blandning av meddelanden i systemets kö med obeställbara meddelanden. Detta är inte alltid optimalt. I flera fall (till exempel säkerhet) kanske du inte vill att en klient ska läsa en annan klients meddelanden från en kö med obeställbara meddelanden. En delad kö med obeställbara meddelanden kräver också att klienter bläddrar igenom kön för att hitta ett meddelande som de har skickat, vilket kan vara oöverkomligt dyrt baserat på antalet meddelanden i kön med obeställbara meddelanden. I WCFNetMsmqBindingMsmqIntegrationBinding, och MSMQ i Windows Vista får du därför en anpassad kö med obeställbara meddelanden (kallas ibland för en programspecifik kö med obeställbara meddelanden).

Den anpassade kön med obeställbara meddelanden ger isolering mellan klienter som delar samma MSMQ-tjänst för att skicka meddelanden.

I Windows Server 2003 och Windows XP tillhandahåller Windows Communication Foundation (WCF) en systemomfattande kö med obeställbara meddelanden för alla köade klientprogram. I Windows Vista tillhandahåller WCF en kö med obeställbara meddelanden för varje klientprogram i kö.

Ange användning av kön med obeställbara meddelanden

En kö med obeställbara meddelanden finns i köhanteraren för det sändande programmet. Den lagrar meddelanden som har upphört att gälla eller som har misslyckats med överföring eller leverans.

Bindningen har följande köegenskaper med obeställbara bokstäver:

Läsa meddelanden från kön med obeställbara meddelanden

Ett program som läser meddelanden från en kö med obeställbara meddelanden liknar en WCF-tjänst som läser från en programkö, förutom följande mindre skillnader:

  • Om du vill läsa meddelanden från en systemtransaktionskö med obeställbara bokstäver måste URI(Uniform Resource Identifier) vara av formatet: net.msmq://localhost/system$; DeadXact.

  • Om du vill läsa meddelanden från en icke-transaktionell kö med obeställbara meddelanden måste URI:n vara av formatet: net.msmq://localhost/system$; DeadLetter.

  • Om du vill läsa meddelanden från en anpassad kö med obeställbara meddelanden måste URI:n vara av formatet:net.msmq://localhost/private/<custom-dlq-name> där custom-dlq-name är namnet på den anpassade kön med obeställbara meddelanden.

Mer information om hur du hanterar köer finns i Tjänstslutpunkter och Köadressering.

WCF-stacken på mottagaren matchar adresser som tjänsten lyssnar på med adressen i meddelandet. Om adresserna matchar skickas meddelandet. annars skickas inte meddelandet. Detta kan orsaka problem vid läsning från kön med obeställbara meddelanden, eftersom meddelanden i kön med obeställbara meddelanden vanligtvis är adresserade till tjänsten och inte till kötjänsten med obeställbara meddelanden. Därför måste tjänsten som läser från kön med obeställbara meddelanden installera ett adressfilter ServiceBehavior som instruerar stacken att matcha alla meddelanden i kön oberoende av adressmottagaren. Mer specifikt måste du lägga till en ServiceBehavior med parametern Any i tjänsten som läser meddelanden från kön med obeställbara meddelanden.

Hantering av giftmeddelanden från kön med obeställbara meddelanden

Hantering av giftmeddelanden finns i köer med obeställbara meddelanden, med vissa villkor. Eftersom du inte kan skapa underköer från systemköer går det ReceiveErrorHandling inte att Moveange till . Observera att om du läser från en anpassad kö med obeställbara meddelanden kan du ha underköer och därför Move är en giltig borttagning för giftmeddelandet.

När ReceiveErrorHandling är inställt på Reject, när du läser från den anpassade kön för obeställbara meddelanden placeras giftmeddelandet i systemets kö med obeställbara meddelanden. Om du läser från systemets kö med obeställbara meddelanden tas meddelandet bort (rensas). Ett avvisande från en systemkö med obeställbara meddelanden i MSMQ släpper (rensar) meddelandet.

Exempel

I följande exempel visas hur du skapar en kö med obeställbara meddelanden och hur du använder den för att bearbeta utgångna meddelanden. Exemplet baseras på exemplet i How to: Exchange Queued Messages with WCF Endpoints (Så här gör du: Exchange-köade meddelanden med WCF-slutpunkter). I följande exempel visas hur du skriver klientkoden till orderbearbetningstjänsten som använder en kö med obeställbara bokstäver för varje program. Exemplet visar också hur du bearbetar meddelanden från kön med obeställbara meddelanden.

Följande är kod för en klient som anger en kö med obeställbara bokstäver för varje program.

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

Följande är kod för klientkonfigurationsfilen.

Följande är kod för en tjänst som bearbetar meddelanden från en kö med obeställbara meddelanden.

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

Följande är kod för konfigurationsfilen för kötjänsten med obeställbara bokstäver.

Se även