Udostępnij za pośrednictwem


Kolejki utraconych komunikatów

W przykładzie DeadLetter pokazano, jak obsługiwać i przetwarzać komunikaty, które zakończyły się niepowodzeniem. Jest on oparty na przykładzie powiązania transacted MSMQ . W tym przykładzie użyto netMsmqBinding powiązania. Usługa jest aplikacją konsolową self-hosted, aby umożliwić obserwowanie usługi odbierających komunikaty w kolejce.

Uwaga

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego tematu.

Uwaga

W tym przykładzie przedstawiono każdą kolejkę utraconych komunikatów aplikacji, która jest dostępna tylko w systemie Windows Vista. Przykład można zmodyfikować tak, aby używał domyślnych kolejek dla msMQ 3.0 w systemach Windows Server 2003 i Windows XP.

W komunikacji w kolejce klient komunikuje się z usługą przy użyciu kolejki. Mówiąc dokładniej, klient wysyła komunikaty do kolejki. Usługa odbiera komunikaty z kolejki. W związku z tym usługa i klient nie muszą być uruchomione w tym samym czasie, aby komunikować się przy użyciu kolejki.

Ponieważ komunikacja w kolejce może obejmować pewną ilość uśpienia, możesz skojarzyć wartość czasu wygaśnięcia z komunikatem, aby upewnić się, że komunikat nie zostanie dostarczony do aplikacji, jeśli minęło czas. Istnieją również przypadki, w których aplikacja musi zostać poinformowana o tym, czy komunikat zakończył się niepowodzeniem. We wszystkich tych przypadkach, takich jak czas wygaśnięcia wiadomości wygasł lub komunikat zakończył się niepowodzeniem, komunikat jest umieszczany w kolejce utraconych komunikatów. Aplikacja wysyłająca może następnie odczytać komunikaty w kolejce utraconych komunikatów i podjąć akcje naprawcze, które wahają się od braku akcji po skorygowanie przyczyn niepowodzenia dostarczania i ponowne wysłanie komunikatu.

Kolejka utraconych komunikatów w powiązaniu NetMsmqBinding jest wyrażona w następujących właściwościach:

  • DeadLetterQueue właściwość do wyrażenia rodzaju kolejki utraconych wiadomości wymaganych przez klienta. Ta wyliczenie ma następujące wartości:

  • None: klient nie wymaga kolejki utraconych komunikatów.

  • System: Kolejka komunikatów utraconych systemu jest używana do przechowywania utraconych komunikatów. Kolejka komunikatów utraconych systemu jest współużytkowany przez wszystkie aplikacje uruchomione na komputerze.

  • Custom: niestandardowa kolejka utraconych komunikatów określona CustomDeadLetterQueue przy użyciu właściwości służy do przechowywania utraconych komunikatów. Ta funkcja jest dostępna tylko w systemie Windows Vista. Jest to używane, gdy aplikacja musi używać własnej kolejki utraconych komunikatów zamiast udostępniać ją innym aplikacjom uruchomionym na tym samym komputerze.

  • CustomDeadLetterQueue właściwość w celu wyrażenia określonej kolejki do użycia jako kolejki utraconych komunikatów. Jest to dostępne tylko w systemie Windows Vista.

W tym przykładzie klient wysyła partię komunikatów do usługi z zakresu transakcji i określa dowolną niską wartość "czas wygaśnięcia" dla tych komunikatów (około 2 sekund). Klient określa również niestandardową kolejkę utraconych komunikatów, która ma być używana do kolejkowania komunikatów, które wygasły.

Aplikacja kliencka może odczytać komunikaty w kolejce utraconych komunikatów i ponowić próbę wysłania komunikatu lub poprawić błąd, który spowodował umieszczenie oryginalnego komunikatu w kolejce utraconych komunikatów i wysłać komunikat. W przykładzie klient wyświetla komunikat o błędzie.

Kontrakt usługi to IOrderProcessor, jak pokazano w poniższym przykładowym kodzie.

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

Kod usługi w przykładzie to powiązanie transacted MSMQ.

Komunikacja z usługą odbywa się w zakresie transakcji. Usługa odczytuje komunikaty z kolejki, wykonuje operację, a następnie wyświetla wyniki operacji. Aplikacja tworzy również kolejkę utraconych komunikatów o utraconych komunikatach.

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

Konfiguracja klienta określa krótki czas trwania komunikatu w celu uzyskania dostępu do usługi. Jeśli nie można przesłać wiadomości w określonym czasie trwania, komunikat wygaśnie i zostanie przeniesiony do kolejki utraconych komunikatów.

Uwaga

Klient może dostarczyć komunikat do kolejki usługi w określonym czasie. Aby upewnić się, że usługa utraconych komunikatów działa, należy uruchomić klienta przed uruchomieniem usługi. Upłynął limit czasu wiadomości i jest dostarczany do usługi utraconych wiadomości.

Aplikacja musi zdefiniować kolejkę, która ma być używana jako kolejka utraconych komunikatów. Jeśli kolejka nie zostanie określona, domyślna kolejka transakcyjnych utraconych komunikatów dla całego systemu jest używana do kolejkowania utraconych komunikatów. W tym przykładzie aplikacja kliencka określa własną kolejkę utraconych komunikatów aplikacji.

<?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>

Usługa komunikatów utraconych odczytuje komunikaty z kolejki utraconych komunikatów. Usługa komunikatów utraconych implementuje IOrderProcessor kontrakt. Jego implementacja nie polega jednak na przetwarzaniu zamówień. Usługa komunikatów utraconych jest usługą klienta i nie ma funkcji przetwarzania zamówień.

Uwaga

Kolejka utraconych komunikatów jest kolejką klienta i jest lokalna dla menedżera kolejek klienta.

Implementacja usługi komunikatów utraconych sprawdza przyczynę niepowodzenia dostarczania komunikatu i podejmuje środki naprawcze. Przyczyna błędu komunikatu jest przechwytywana w dwóch wyliczeniach i DeliveryFailureDeliveryStatus. Możesz pobrać element MsmqMessageProperty z pliku OperationContext , jak pokazano w poniższym przykładowym kodzie:

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

Komunikaty w kolejce utraconych komunikatów to komunikaty, które są adresowane do usługi, która przetwarza komunikat. W związku z tym, gdy usługa komunikatów utraconych odczytuje komunikaty z kolejki, warstwa kanału Windows Communication Foundation (WCF) znajduje niezgodność w punktach końcowych i nie wysyła komunikatu. W takim przypadku komunikat jest kierowany do usługi przetwarzania zamówień, ale jest odbierany przez usługę komunikatów utraconych wiadomości. Aby otrzymać komunikat skierowany do innego punktu końcowego, filtr adresu zgodny z dowolnym adresem jest określony w elemecie ServiceBehavior. Jest to wymagane do pomyślnego przetwarzania komunikatów odczytywanych z kolejki utraconych komunikatów.

W tym przykładzie usługa komunikatów utraconych wysyła ponownie komunikat, jeśli przyczyną niepowodzenia jest przekroczenie limitu czasu komunikatu. Ze wszystkich innych powodów wyświetla błąd dostarczania, jak pokazano w poniższym przykładowym kodzie:

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

Poniższy przykład przedstawia konfigurację komunikatu utraconych komunikatów:

<?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>

W przypadku uruchamiania przykładu istnieje 3 pliki wykonywalne do uruchomienia, aby zobaczyć, jak działa kolejka utraconych komunikatów dla każdej aplikacji; klient, usługa i usługa utraconych komunikatów, która odczytuje z kolejki utraconych komunikatów dla każdej aplikacji i ponownie wysyła komunikat do usługi. Wszystkie są aplikacjami konsoli z danymi wyjściowymi w oknach konsoli.

Uwaga

Ponieważ kolejkowanie jest używane, klient i usługa nie muszą być uruchomione w tym samym czasie. Możesz uruchomić klienta, zamknąć go, a następnie uruchomić usługę i nadal odbierać komunikaty. Należy uruchomić usługę i zamknąć ją, aby można było utworzyć kolejkę.

Podczas uruchamiania klienta klient wyświetla komunikat:

Press <ENTER> to terminate client.

Klient próbował wysłać komunikat, ale z krótkim limitem czasu, komunikat wygasł i jest teraz w kolejce utraconych komunikatów dla każdej aplikacji.

Następnie uruchomisz usługę utraconych komunikatów, która odczytuje komunikat i wyświetla kod błędu i ponownie wysyła komunikat z powrotem do usługi.

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

Usługa zostanie uruchomiona, a następnie odczytuje komunikat resent i przetworzy go.

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

Aby skonfigurować, skompilować i uruchomić przykład

  1. Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.

  2. Jeśli usługa jest uruchomiona jako pierwsza, sprawdzi, czy kolejka jest obecna. Jeśli kolejka nie jest obecna, usługa utworzy je. Możesz najpierw uruchomić usługę, aby utworzyć kolejkę, lub utworzyć jedną za pośrednictwem menedżera kolejki MSMQ. Wykonaj następujące kroki, aby utworzyć kolejkę w systemie Windows 2008.

    1. Otwórz Menedżer serwera w programie Visual Studio 2012.

    2. Rozwiń kartę Funkcje .

    3. Kliknij prawym przyciskiem myszy pozycję Kolejki komunikatów prywatnych, a następnie wybierz pozycję Nowa, Prywatna kolejka.

    4. Zaznacz pole Transakcyjne.

    5. Wprowadź ServiceModelSamplesTransacted jako nazwę nowej kolejki.

  3. Aby skompilować wersję rozwiązania w języku C# lub Visual Basic .NET, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).

  4. Aby uruchomić przykład w konfiguracji pojedynczej lub między komputerami, odpowiednio zmień nazwy kolejek, zastępując localhost pełną nazwą komputera i postępuj zgodnie z instrukcjami w sekcji Uruchamianie przykładów programu Windows Communication Foundation.

Aby uruchomić przykład na komputerze przyłączonym do grupy roboczej

  1. Jeśli komputer nie jest częścią domeny, wyłącz zabezpieczenia transportu, ustawiając tryb uwierzytelniania i poziom ochrony na, jak None pokazano w następującej przykładowej konfiguracji:

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

    Upewnij się, że punkt końcowy jest skojarzony z powiązaniem, ustawiając atrybut punktu końcowego bindingConfiguration .

  2. Przed uruchomieniem przykładu upewnij się, że zmieniono konfigurację w usłudze DeadLetterService, serwerze i kliencie.

    Uwaga

    Ustawienie security mode na None jest równoważne ustawieniu MsmqAuthenticationMode, MsmqProtectionLevel i Message zabezpieczeń na None.

Komentarze

Domyślnie w przypadku netMsmqBinding transportu powiązania zabezpieczenia są włączone. Dwie właściwości MsmqAuthenticationMode i MsmqProtectionLevel, razem określają typ zabezpieczeń transportu. Domyślnie tryb uwierzytelniania jest ustawiony na Windows wartość , a poziom ochrony jest ustawiony na Signwartość . Aby usługa MSMQ zapewniała funkcję uwierzytelniania i podpisywania, musi być częścią domeny. Jeśli uruchomisz ten przykład na komputerze, który nie jest częścią domeny, zostanie wyświetlony następujący błąd: "Wewnętrzny certyfikat kolejkowania komunikatów użytkownika nie istnieje".