Delen via


Wachtrijen voor onbestelbare berichten gebruiken voor het afhandelen van fouten bij het overdragen van berichten

Berichten in de wachtrij kunnen de bezorging mislukken. Deze mislukte berichten worden vastgelegd in een wachtrij met onbestelbare berichten. De mislukte levering kan worden veroorzaakt door redenen zoals netwerkfouten, een verwijderde wachtrij, een volledige wachtrij, verificatiefout of een fout bij het leveren op tijd.

Berichten in de wachtrij kunnen lange tijd in de wachtrij blijven als de ontvangende toepassing deze niet tijdig uit de wachtrij leest. Dit gedrag is mogelijk niet geschikt voor tijdgevoelige berichten. Tijdgevoelige berichten hebben een TTL-eigenschap (Time to Live) ingesteld in de binding in de wachtrij. Dit geeft aan hoe lang de berichten in de wachtrij kunnen staan voordat ze moeten verlopen. Verlopen berichten worden verzonden naar een speciale wachtrij, de wachtrij met dode letters. Berichten kunnen ook om andere redenen in een wachtrij met wachtrijen worden geplaatst, zoals het overschrijden van een wachtrijquotum of vanwege een verificatiefout.

Over het algemeen schrijven toepassingen compensatielogica voor het lezen van berichten uit de wachtrij en foutredenen. De compensatielogica is afhankelijk van de oorzaak van de fout. In het geval van een verificatiefout kunt u bijvoorbeeld het certificaat corrigeren dat is gekoppeld aan het bericht en het bericht opnieuw verzenden. Als de levering is mislukt omdat het quotum voor de doelwachtrij is bereikt, kunt u de levering opnieuw instellen in de hoop dat het quotumprobleem is opgelost.

De meeste wachtrijsystemen hebben een systeembrede wachtrij met dode letters waarin alle mislukte berichten van dat systeem worden opgeslagen. Message Queuing (MSMQ) biedt twee wachtrijen voor dead-letter in het systeem: een transactionele wachtrij voor dead-letter waarin berichten worden opgeslagen die niet in de transactionele wachtrij zijn afgeleverd en een wachtrij met niet-transactionele dode brieven die berichten opslaan die niet aan de niet-transactionele wachtrij zijn geleverd. Als twee clients berichten verzenden naar twee verschillende services en daarom verschillende wachtrijen in WCF dezelfde MSMQ-service delen om te verzenden, is het mogelijk om een combinatie van berichten te hebben in de wachtrij met dode brieven van het systeem. Dit is niet altijd optimaal. In verschillende gevallen (beveiliging, bijvoorbeeld), wilt u mogelijk niet dat één client de berichten van een andere client leest uit een wachtrij met dode letters. Voor een gedeelde wachtrij met dode letters moeten clients ook door de wachtrij bladeren om een bericht te vinden dat ze hebben verzonden, wat te duur kan zijn op basis van het aantal berichten in de wachtrij met dode letters. Daarom bieden WCFNetMsmqBindingMsmqIntegrationBinding, en MSMQ op Windows Vista een aangepaste wachtrij met dode letters (ook wel een toepassingsspecifieke wachtrij voor dode letters genoemd).

De aangepaste wachtrij met dode letters biedt isolatie tussen clients die dezelfde MSMQ-service delen om berichten te verzenden.

Op Windows Server 2003 en Windows XP biedt Windows Communication Foundation (WCF) een systeembrede wachtrij met dode letters voor alle clienttoepassingen in de wachtrij. Op Windows Vista biedt WCF een wachtrij met dode letters voor elke clienttoepassing in de wachtrij.

Het gebruik van de wachtrij voor dode letters opgeven

Een wachtrij met dode letters bevindt zich in de wachtrijbeheerder van de verzendende toepassing. Er worden berichten opgeslagen die zijn verlopen of die niet zijn overgedragen of geleverd.

De binding heeft de volgende wachtrijeigenschappen voor onbestelbare letters:

Berichten lezen uit de wachtrij voor onbestelbare berichten

Een toepassing die berichten uit een wachtrij met dode letters leest, is vergelijkbaar met een WCF-service die uit een toepassingswachtrij leest, met uitzondering van de volgende kleine verschillen:

  • Als u berichten wilt lezen uit een wachtrij voor transactionele dode letters, moet de URI (Uniform Resource Identifier) van het formulier zijn: net.msmq://localhost/system$; DeadXact.

  • Als u berichten wilt lezen uit een niet-transactionele wachtrij met dode letters, moet de URI van het formulier zijn: net.msmq://localhost/system$; DeadLetter.

  • Als u berichten wilt lezen uit een aangepaste wachtrij met dead-letter, moet de URI de naam hebben van de URI:net.msmq://localhost/private/<custom-dlq-name waarbij custom-dlq-name> de naam is van de aangepaste wachtrij met dode letters.

Zie Service-eindpunten en Wachtrijadressering voor meer informatie over het adresseren van wachtrijen.

De WCF-stack op de ontvanger komt overeen met adressen waarop de service luistert met het adres in het bericht. Als de adressen overeenkomen, wordt het bericht verzonden; zo niet, dan wordt het bericht niet verzonden. Dit kan problemen veroorzaken bij het lezen vanuit de wachtrij met dode letters, omdat berichten in de wachtrij met dode letters meestal worden geadresseerd aan de service en niet aan de service voor wachtrijen met dode letters. Daarom moet de service die uit de wachtrij met dode letters leest, een adresfilter ServiceBehavior installeren waarmee de stack wordt geïnstrueerd om alle berichten in de wachtrij onafhankelijk van de geadresseerde te vinden. U moet met name een ServiceBehavior met de Any parameter toevoegen aan de service voor het lezen van berichten uit de wachtrij met dode letters.

Afhandeling van gifberichten uit de wachtrij voor onbestelbare berichten

De verwerking van gifberichten is beschikbaar in wachtrijen met onbestelbare berichten, met een aantal voorwaarden. Omdat u geen subwachtrijen kunt maken op basis van systeemwachtrijen, kunt u de wachtrij niet instellen Moveop ReceiveErrorHandling . Houd er rekening mee dat als u leest vanuit een aangepaste wachtrij met dode letters, subwachtrijen kunt hebben en daarom Move een geldige verwijdering is voor het gifbericht.

Wanneer ReceiveErrorHandling dit is ingesteld op Reject, bij het lezen uit de aangepaste wachtrij voor dode letters, wordt het gifbericht in de wachtrij met dode letters geplaatst. Als het bericht wordt gelezen uit de wachtrij met dode letters van het systeem, wordt het bericht verwijderd (leeggemaakt). Een afwijzing uit een wachtrij met dode letters in MSMQ laat het bericht vallen (opschonen).

Opmerking

In het volgende voorbeeld ziet u hoe u een wachtrij met dode letters maakt en hoe u deze kunt gebruiken om verlopen berichten te verwerken. Het voorbeeld is gebaseerd op het voorbeeld in Procedure: Berichten in wachtrij uitwisselen met WCF-eindpunten. In het volgende voorbeeld ziet u hoe u de clientcode schrijft naar de orderverwerkingsservice die gebruikmaakt van een wachtrij met onbestelbare letters voor elke toepassing. In het voorbeeld ziet u ook hoe u berichten uit de wachtrij voor onbestelbare berichten verwerkt.

Hier volgt code voor een client die een wachtrij met dode letters voor elke toepassing opgeeft.

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

Hier volgt code voor het clientconfiguratiebestand.

Hier volgt code voor het verwerken van berichten van een service uit een wachtrij met dode letters.

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

Hier volgt code voor het configuratiebestand voor de wachtrijservice voor dode letters.

Zie ook