Dela via


Köer med obeställbara meddelanden

DeadLetter-exemplet visar hur du hanterar och bearbetar meddelanden som har misslyckats med leveransen. Den baseras på transacted MSMQ Binding-exemplet . Det här exemplet använder bindningen netMsmqBinding . Tjänsten är ett konsolprogram med egen värd som gör att du kan observera att tjänsten tar emot köade meddelanden.

Kommentar

Installationsproceduren och bygginstruktionerna för det här exemplet finns i slutet av det här avsnittet.

Kommentar

Det här exemplet visar varje kö med obeställbara meddelanden som endast är tillgänglig i Windows Vista. Exemplet kan ändras för att använda standardsystemomfattande köer för MSMQ 3.0 på Windows Server 2003 och Windows XP.

I kökommunikation kommunicerar klienten till tjänsten med hjälp av en kö. Mer exakt skickar klienten meddelanden till en kö. Tjänsten tar emot meddelanden från kön. Tjänsten och klienten behöver därför inte köras samtidigt för att kommunicera med en kö.

Eftersom kommunikation i kö kan omfatta en viss mängd vilande värden kanske du vill associera ett time-to-live-värde i meddelandet för att säkerställa att meddelandet inte levereras till programmet om det har passerat tiden. Det finns också fall där ett program måste informeras om ett meddelande misslyckades. I alla dessa fall, till exempel när time-to-live på meddelandet har upphört att gälla eller meddelandet misslyckades med leveransen, placeras meddelandet i en kö med obeställbara meddelanden. Det sändande programmet kan sedan läsa meddelandena i kön med obeställbara meddelanden och vidta korrigerande åtgärder som sträcker sig från ingen åtgärd till att korrigera orsakerna till misslyckad leverans och skicka meddelandet igen.

Kön med obeställbara bokstäver i bindningen NetMsmqBinding uttrycks i följande egenskaper:

  • DeadLetterQueue för att uttrycka den typ av kö med obeställbara meddelanden som krävs av klienten. Den här uppräkningen har följande värden:

  • None: Ingen kö med obeställbara meddelanden krävs av klienten.

  • System: Systemets kö med obeställbara meddelanden används för att lagra döda meddelanden. Systemets kö för obeställbara meddelanden delas av alla program som körs på datorn.

  • Custom: En anpassad kö med obeställbara meddelanden som anges med egenskapen CustomDeadLetterQueue används för att lagra döda meddelanden. Den här funktionen är endast tillgänglig i Windows Vista. Detta används när programmet måste använda sin egen kö med obeställbara meddelanden i stället för att dela den med andra program som körs på samma dator.

  • CustomDeadLetterQueue egenskap för att uttrycka den specifika kö som ska användas som en kö med obeställbara meddelanden. Detta är endast tillgängligt i Windows Vista.

I det här exemplet skickar klienten en batch med meddelanden till tjänsten inom omfånget för en transaktion och anger ett godtyckligt lågt värde för "time-to-live" för dessa meddelanden (cirka 2 sekunder). Klienten anger också en anpassad kö med obeställbara meddelanden som ska användas för att ange de meddelanden som har upphört att gälla.

Klientprogrammet kan läsa meddelandena i kön med obeställbara meddelanden och antingen försöka skicka meddelandet igen eller korrigera felet som gjorde att det ursprungliga meddelandet placerades i kön med obeställbara meddelanden och skicka meddelandet. I exemplet visar klienten ett felmeddelande.

Tjänstkontraktet är IOrderProcessor, som du ser i följande exempelkod.

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples")]
public interface IOrderProcessor
{
    [OperationContract(IsOneWay = true)]
    void SubmitPurchaseOrder(PurchaseOrder po);
}

Tjänstkoden i exemplet är transacted MSMQ-bindningen.

Kommunikation med tjänsten sker inom omfånget för en transaktion. Tjänsten läser meddelanden från kön, utför åtgärden och visar sedan resultatet av åtgärden. Programmet skapar också en kö med obeställbara bokstäver för meddelanden med obeställbara meddelanden.

//The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.

//Client implementation code.
class Client
{
    static void Main()
    {
        // Get MSMQ queue name from app settings in configuration
        string deadLetterQueueName = ConfigurationManager.AppSettings["deadLetterQueueName"];

        // Create the transacted MSMQ queue for storing dead message if necessary.
        if (!MessageQueue.Exists(deadLetterQueueName))
            MessageQueue.Create(deadLetterQueueName, true);

        // Create a proxy with given client endpoint configuration
        OrderProcessorClient client = new OrderProcessorClient("OrderProcessorEndpoint");

        // 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();

        Console.WriteLine();
        Console.WriteLine("Press <ENTER> to terminate client.");
        Console.ReadLine();
    }
}

Klientens konfiguration anger en kort varaktighet för meddelandet att nå tjänsten. Om meddelandet inte kan överföras inom den angivna varaktigheten upphör meddelandet att gälla och flyttas till kön med obeställbara meddelanden.

Kommentar

Det är möjligt för klienten att leverera meddelandet till tjänstkön inom den angivna tiden. För att säkerställa att tjänsten med obeställbara bokstäver fungerar bör du köra klienten innan du startar tjänsten. Meddelandet överskrider tidsgränsen och levereras till tjänsten med obeställbara meddelanden.

Programmet måste definiera vilken kö som ska användas som kö med obeställbara meddelanden. Om ingen kö har angetts används den systemomfattande transaktionella kön för obeställbara meddelanden i kö. I det här exemplet anger klientprogrammet sin egen kö med obeställbara meddelanden.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- use appSetting to configure MSMQ Dead Letter queue name -->
    <add key="deadLetterQueueName" value=".\private$\ServiceModelSamplesOrdersAppDLQ"/>
  </appSettings>

  <system.serviceModel>
    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                binding="netMsmqBinding"
                bindingConfiguration="PerAppDLQBinding"
                contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="PerAppDLQBinding"
                 deadLetterQueue="Custom"
                 customDeadLetterQueue="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                 timeToLive="00:00:02"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>

</configuration>

Meddelandetjänsten med obeställbara meddelanden läser meddelanden från kön med obeställbara meddelanden. Meddelandetjänsten med obeställbara meddelanden implementerar IOrderProcessor kontraktet. Dess implementering är dock inte att bearbeta beställningar. Meddelandetjänsten med obeställbara meddelanden är en klienttjänst och har inte möjlighet att bearbeta beställningar.

Kommentar

Kön med obeställbara meddelanden är en klientkö och är lokal för klientköhanteraren.

Implementeringen av meddelandetjänsten med obeställbara meddelanden kontrollerar orsaken till att ett meddelande misslyckades och vidtar korrigerande åtgärder. Orsaken till ett meddelandefel samlas in i två uppräkningar och DeliveryFailureDeliveryStatus. Du kan hämta MsmqMessageProperty från enligt OperationContext följande exempelkod:

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();
    …
}

Meddelanden i kön med obeställbara meddelanden är meddelanden som är adresserade till den tjänst som bearbetar meddelandet. När meddelandetjänsten med obeställbara meddelanden läser meddelanden från kön hittar därför WCF-kanallagret (Windows Communication Foundation) felmatchningen i slutpunkterna och skickar inte meddelandet. I det här fallet är meddelandet adresserat till orderbearbetningstjänsten men tas emot av meddelandetjänsten med obeställbara meddelanden. Om du vill ta emot ett meddelande som är adresserat till en annan slutpunkt anges ett adressfilter som matchar alla adresser i ServiceBehavior. Detta krävs för att bearbeta meddelanden som lästs från kön med obeställbara meddelanden.

I det här exemplet skickar meddelandetjänsten med obeställbara meddelanden meddelandet igen om orsaken till felet är att tidsgränsen för meddelandet överskrids. Av alla andra orsaker visas leveransfelet, som du ser i följande exempelkod:

// 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 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 app. 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();
        }
    }
}

Följande exempel visar konfigurationen för ett meddelande med obeställbara meddelanden:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.PurchaseOrderDLQService">
        <!-- Define NetMsmqEndpoint in this case, DLQ end point to read messages-->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                  binding="netMsmqBinding"
                  bindingConfiguration="DefaultBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
      </service>
    </services>

    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                 address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                 binding="netMsmqBinding"
                 bindingConfiguration="SystemDLQBinding"
                 contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="DefaultBinding" />
        <binding name="SystemDLQBinding"
                 deadLetterQueue="System"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>

När du kör exemplet finns det tre körbara filer att köra för att se hur kön med obeställbara meddelanden fungerar för varje program. klienten, tjänsten och en tjänst med obeställbara meddelanden som läser från kön med obeställbara meddelanden för varje program och skickar meddelandet till tjänsten igen. Alla är konsolprogram med utdata i konsolfönster.

Kommentar

Eftersom köer används behöver klienten och tjänsten inte vara igång samtidigt. Du kan köra klienten, stänga av den och sedan starta tjänsten och fortfarande ta emot dess meddelanden. Du bör starta tjänsten och stänga av den så att kön kan skapas.

När klienten körs visas meddelandet:

Press <ENTER> to terminate client.

Klienten försökte skicka meddelandet, men med en kort tidsgräns upphörde meddelandet att gälla och köas nu i kön med obeställbara meddelanden för varje program.

Sedan kör du tjänsten dead-letter, som läser meddelandet och visar felkoden och skickar tillbaka meddelandet till tjänsten.

The dead letter service is ready.
Press <ENTER> to terminate service.

Submitting purchase order did not succeed
Message Delivery Status: InDoubt
Message Delivery Failure: ReachQueueTimeout

Purchase order Time To Live expired
Trying to resend the message
Purchase order resent

Tjänsten startar och läser sedan meddelandet och bearbetar det igen.

The service is ready.
Press <ENTER> to terminate service.

Processing Purchase Order: 97897eff-f926-4057-a32b-af8fb11b9bf9
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 54 of Blue Widget @unit price: $29.99
                Order LineItem: 890 of Red Widget @unit price: $45.89
        Total cost of this order: $42461.56
        Order status: Pending

Så här konfigurerar du, skapar och kör exemplet

  1. Kontrollera att du har utfört engångsinstallationsproceduren för Windows Communication Foundation-exempel.

  2. Om tjänsten körs först kontrollerar den att kön finns. Om kön inte finns skapar tjänsten en. Du kan köra tjänsten först för att skapa kön, eller så kan du skapa en via MSMQ Queue Manager. Följ de här stegen för att skapa en kö i Windows 2008.

    1. Öppna Serverhanteraren i Visual Studio 2012.

    2. Expandera fliken Funktioner .

    3. Högerklicka på Privata meddelandeköer och välj Ny privat .

    4. Markera rutan Transaktionell .

    5. Ange ServiceModelSamplesTransacted som namnet på den nya kön.

  3. Om du vill skapa C# eller Visual Basic .NET-versionen av lösningen följer du anvisningarna i Skapa Windows Communication Foundation-exempel.

  4. Om du vill köra exemplet i ett konfigurationsnamn för en eller flera datorer ändrar du könamnen på rätt sätt genom att ersätta localhost med datorns fullständiga namn och följa anvisningarna i Köra Windows Communication Foundation-exempel.

Så här kör du exemplet på en dator som är ansluten till en arbetsgrupp

  1. Om datorn inte ingår i en domän inaktiverar du transportsäkerheten genom att ställa in autentiseringsläget och skyddsnivån på None enligt följande exempelkonfiguration:

    <bindings>
        <netMsmqBinding>
            <binding name="TransactedBinding">
                <security mode="None"/>
            </binding>
        </netMsmqBinding>
    </bindings>
    

    Kontrollera att slutpunkten är associerad med bindningen genom att ange slutpunktens bindingConfiguration attribut.

  2. Se till att du ändrar konfigurationen på DeadLetterService, servern och klienten innan du kör exemplet.

    Kommentar

    Inställningen security mode till None motsvarar inställningen MsmqAuthenticationModeoch MsmqProtectionLevelMessage säkerheten för None.

Kommentarer

Som standard med bindningstransporten netMsmqBinding är säkerheten aktiverad. Två egenskaper, MsmqAuthenticationMode och MsmqProtectionLevel, bestämmer tillsammans typen av transportsäkerhet. Som standard är autentiseringsläget inställt på Windows och skyddsnivån är inställd på Sign. För att MSMQ ska kunna tillhandahålla autentiserings- och signeringsfunktionen måste den vara en del av en domän. Om du kör det här exemplet på en dator som inte ingår i en domän får du följande fel: "Användarens interna meddelandeköcertifikat finns inte".