Partilhar via


Filas de Mensagens Mortas

O exemplo DeadLetter demonstra como manipular e processar mensagens que falharam na entrega. É baseado no exemplo de Ligação MSMQ Transacionada. Este exemplo usa a netMsmqBinding vinculação. O serviço é um aplicativo de console auto-hospedado para permitir que você observe o serviço recebendo mensagens na fila.

Observação

O procedimento de configuração e as instruções de compilação para este exemplo estão localizados no final deste tópico.

Observação

Este exemplo demonstra a fila de mensagens mortas de cada aplicação que está disponível apenas no Windows Vista. O exemplo pode ser modificado para usar as filas padrão em todo o sistema para MSMQ 3.0 no Windows Server 2003 e Windows XP.

Na comunicação em fila, o cliente comunica-se com o serviço mediante uma fila. Mais precisamente, o cliente envia mensagens para uma fila. O serviço recebe mensagens da fila. O serviço e o cliente, portanto, não precisam estar em execução simultaneamente para se comunicarem usando uma fila.

Como a comunicação em fila pode envolver uma certa quantidade de inatividade, convém associar um valor de tempo de vida à mensagem para garantir que, caso tenha ultrapassado o tempo, a mensagem não seja entregue à aplicação. Há também casos em que um aplicativo deve ser informado se uma mensagem falhou na entrega. Em todos esses casos, como quando o tempo de vida da mensagem expirou ou a mensagem falhou na entrega, a mensagem é colocada em uma fila de letra morta. O aplicativo de envio pode então ler as mensagens na fila de mensagens mortas e tomar ações corretivas que variam de nenhuma ação para corrigir os motivos da falha na entrega e reenviar a mensagem.

A fila de letras mortas na NetMsmqBinding associação é expressa nas seguintes propriedades:

  • DeadLetterQueue propriedade para expressar o tipo de fila de mensagens mortas exigido pelo cliente. Esta enumeração tem os seguintes valores:

  • None: Nenhuma fila de mensagens mortas é exigida pelo cliente.

  • System: A fila de mensagens mortas do sistema é usada para armazenar mensagens inativas. A fila de mensagens mortas do sistema é compartilhada por todos os aplicativos em execução no computador.

  • Custom: Uma fila de letras mortas personalizada especificada usando a CustomDeadLetterQueue propriedade é usada para armazenar mensagens mortas. Esta funcionalidade só está disponível no Windows Vista. Isso é usado quando a aplicação deve utilizar a sua própria fila de mensagens mortas em vez de a partilhar com outras aplicações em execução no mesmo computador.

  • CustomDeadLetterQueue para expressar a fila específica a ser usada como uma fila de letras mortas. Isso está disponível apenas no Windows Vista.

Neste exemplo, o cliente envia um lote de mensagens para o serviço de dentro do escopo de uma transação e especifica um valor arbitrariamente baixo para "time-to-live" para essas mensagens (cerca de 2 segundos). O cliente também especifica uma fila de mensagens mortas personalizada a ser usada para enfileirar as mensagens que expiraram.

O aplicativo cliente pode ler as mensagens na fila de mensagens mortas e tentar enviar novamente a mensagem ou corrigir o erro que fez com que a mensagem original fosse colocada na fila de mensagens mortas e enviasse a mensagem. No exemplo, o cliente exibe uma mensagem de erro.

O contrato de serviço é IOrderProcessor, conforme mostrado no código de exemplo a seguir.

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

O código de serviço no exemplo é o da Ligação MSMQ Transacionada.

A comunicação com o serviço ocorre no âmbito de uma transação. O serviço lê mensagens da fila, executa a operação e, em seguida, exibe os resultados da operação. O aplicativo também cria uma fila de mensagens de letra morta.

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

A configuração do cliente especifica uma curta duração para que a mensagem chegue ao serviço. Se a mensagem não puder ser transmitida dentro da duração especificada, a mensagem expirará e será movida para a fila de mensagens mortas.

Observação

É possível que o cliente entregue a mensagem para a fila de serviço dentro do tempo especificado. Para garantir que você veja o serviço de carta morta em ação, execute o cliente antes de iniciar o serviço. A mensagem ultrapassa o tempo limite e é entregue ao serviço de mensagens não entregues.

O aplicativo deve definir qual fila usar como sua fila de letra morta. Se nenhuma fila for especificada, a fila de letras mortas transacional padrão em todo o sistema será usada para enfileirar mensagens inativas. Neste exemplo, o aplicativo cliente especifica sua própria fila de letras mortas do aplicativo.

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

O serviço de mensagens não entregues lê mensagens da fila de mensagens não entregues. O serviço de mensagens mortas executa o IOrderProcessor contrato. Sua implementação, no entanto, não é para processar pedidos. O serviço de mensagens mortas é um serviço de cliente e não tem a facilidade de processar pedidos.

Observação

A fila de mensagens mortas é uma fila de cliente e é local do gestor de filas do cliente.

A implementação do serviço de mensagens mortas verifica o motivo pelo qual uma mensagem falhou na entrega e toma medidas corretivas. O motivo de uma falha de mensagem é capturado em duas enumerações DeliveryFailure e DeliveryStatus. Você pode recuperar o MsmqMessageProperty do OperationContext conforme mostrado no exemplo de código a seguir:

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

As mensagens na fila de mensagens mortas são mensagens endereçadas ao serviço que está processando a mensagem. Portanto, quando o serviço de mensagens de dead-letter lê mensagens da fila, a camada de canal do Windows Communication Foundation (WCF) localiza a incompatibilidade nos pontos de extremidade e não encaminha a mensagem. Nesse caso, a mensagem é endereçada ao serviço de processamento de pedidos, mas é recebida pelo serviço de mensagens mortas. Para receber uma mensagem endereçada a um ponto de extremidade diferente, um filtro de endereço para corresponder a qualquer endereço é especificado no ServiceBehavior. Isso é necessário para processar com êxito as mensagens que são lidas da fila de mensagens mortas.

Neste exemplo, o serviço de mensagem de letra morta reenvia a mensagem se o motivo da falha for o tempo limite da mensagem. Para todos os outros motivos, exibe a falha de entrega, como mostrado no código de exemplo a seguir:

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

O exemplo a seguir mostra a configuração de uma mensagem de letra morta:

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

Ao executar o exemplo, existem 3 executáveis para se ver como a fila de mensagens mortas funciona para cada aplicação: o cliente, o serviço e um serviço de fila de mensagens mortas que lê a fila para cada aplicação e reenvia a mensagem para o serviço. Todos são aplicativos de console com saída em janelas de console.

Observação

Como o enfileiramento está em uso, o cliente e o serviço não precisam estar funcionando ao mesmo tempo. Você pode executar o cliente, desligá-lo e, em seguida, iniciar o serviço e ele ainda recebe suas mensagens. Você deve iniciar o serviço e desligá-lo para que a fila possa ser criada.

Ao executar o cliente, o cliente exibe a mensagem:

Press <ENTER> to terminate client.

O cliente tentou enviar a mensagem, mas com um curto tempo limite, a mensagem expirou e agora está na fila de mensagens mortas para cada aplicativo.

Em seguida, execute o serviço de mensagens perdidas, o que lê a mensagem, exibe o código de erro e reenvia a mensagem novamente para o serviço.

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

O serviço é iniciado e, em seguida, lê a mensagem reenviada e processa-a.

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

Para configurar, compilar e executar o exemplo

  1. Verifique se você executou o procedimento de instalação do One-Time para os exemplos do Windows Communication Foundation.

  2. Se o serviço for executado primeiro, ele verificará se a fila está presente. Se a fila não estiver presente, o serviço criará uma. Você pode executar o serviço primeiro para criar a fila ou pode criar uma por meio do Gerenciador de Filas MSMQ. Siga estas etapas para criar uma fila no Windows 2008.

    1. Abra o Gerenciador do Servidor no Visual Studio 2012.

    2. Expanda a guia Recursos.

    3. Clique com o botão direito do mouse Filas de Mensagens Privadase selecione Nova, Fila Privada.

    4. Marque a caixa Transacional .

    5. Digite ServiceModelSamplesTransacted como o nome da nova fila.

  3. Para criar a edição C# ou Visual Basic .NET da solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.

  4. Para executar o exemplo numa configuração de um único computador ou cruzada, altere adequadamente os nomes das filas, substituindo localhost pelo nome completo do computador, e siga as instruções em Executar os exemplos do Windows Communication Foundation.

Para executar o exemplo em um computador associado a um grupo de trabalho

  1. Se o computador não fizer parte de um domínio, desative a segurança de transporte definindo o modo de autenticação e o nível de proteção como None mostrado na seguinte configuração de exemplo:

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

    Verifique se o ponto de extremidade está associado à associação definindo o atributo do ponto de bindingConfiguration extremidade.

  2. Certifique-se de alterar a configuração no DeadLetterService, no servidor e no cliente antes de executar o exemplo.

    Observação

    Definir security mode como None é equivalente a definir MsmqAuthenticationMode, MsmqProtectionLevel e Message segurança como None.

Observações

Por padrão, com o transporte de binding netMsmqBinding, a segurança está habilitada. Duas propriedades, MsmqAuthenticationMode e MsmqProtectionLevel, juntas determinam o tipo de segurança de transporte. Por padrão, o modo de autenticação é definido como Windows e o nível de proteção é definido como Sign. Para que o MSMQ forneça o recurso de autenticação e assinatura, ele deve fazer parte de um domínio. Se você executar este exemplo em um computador que não faz parte de um domínio, você receberá o seguinte erro: "O certificado interno de enfileiramento de mensagens do usuário não existe".