Compartilhar via


Filas de mensagens de inatividade

A amostra de DeadLetter demonstra como lidar e processar mensagens que falharam na entrega. Ela é baseada na amostra de Associação transacionada do MSMQ. Esta amostra usa a associação netMsmqBinding. O serviço é um aplicativo de console auto-hospedado para permitir que você observe o serviço que está recebendo mensagens na fila.

Observação

O procedimento de instalação e as instruções de compilação dessa amostra estão no final deste tópico.

Observação

Esta amostra demonstra cada fila de mensagens de inatividade do aplicativo que só está disponível no Windows Vista. A amostra pode ser modificada para usar as filas padrão em todo o sistema para MSMQ 3.0 no Windows Server 2003 e Windows XP.

Na comunicação na fila, o cliente se comunica com o serviço usando uma fila. Mais precisamente, o cliente envia mensagens para uma fila. O serviço recebe mensagens da fila. Portanto, o serviço e o cliente não precisam estar em execução ao mesmo tempo para se comunicar usando uma fila.

Como a comunicação enfileirada pode envolver uma determinada quantidade de dormência, talvez você queira associar um valor de vida útil à mensagem para garantir que a mensagem não seja entregue ao aplicativo se ela tiver passado do tempo. Também há casos em que um aplicativo deve ser informado se uma mensagem falhou na entrega. Em todos esses casos, como quando o tempo de vida útil na mensagem expirou ou a mensagem falhou na entrega, a mensagem é colocada em uma fila de mensagens de inatividade. Em seguida, o aplicativo de envio pode ler as mensagens na fila de mensagens de inatividade e executar ações corretivas que variam de nenhuma ação para corrigir os motivos da entrega com falha e reenviar a mensagem.

A fila de mensagens de inatividade na associação NetMsmqBinding é expressa nas seguintes propriedades:

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

  • None: nenhuma fila de mensagens de inatividade é exigida pelo cliente.

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

  • Custom: uma fila de mensagens de inatividade personalizada especificada usando a propriedade CustomDeadLetterQueue é usada para armazenar mensagens de inatividade. Este recurso só está disponível no Windows Vista. Isso é usado quando o aplicativo deve usar sua própria fila de mensagens de inatividade em vez de compartilhá-la com outros aplicativos em execução no mesmo computador.

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

Nesta amostra, o cliente envia um lote de mensagens para o serviço de dentro do escopo de uma transação e especifica um valor baixo arbitrariamente para a "vida útil" para essas mensagens (cerca de 2 segundos). O cliente também especifica uma fila de mensagens de inatividade personalizada a ser usada para enfileirar as mensagens que expiraram.

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

O contrato de serviço IOrderProcessor, como mostrado no código de amostra a seguir.

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

O código de serviço na amostra é o da Associação transacionada do MSMQ.

A comunicação com o serviço ocorre dentro do escopo de uma transação. O serviço lê mensagens da fila, executa a operação e exibe os resultados da operação. O aplicativo também cria uma fila de mensagens de inatividade para mensagens de inatividade.

//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 de inatividade.

Observação

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

O aplicativo deve definir qual fila usar como sua fila de mensagens de inatividade. Se nenhuma fila for especificada, a fila de mensagens de inatividade transacionais em todo o sistema padrão será usada para enfileirar mensagens de inatividade. Neste exemplo, o aplicativo cliente especifica sua própria fila de mensagens de inatividade 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 de inatividade lê mensagens da fila de mensagens de inatividade. O serviço de mensagem de inatividade implementa o contrato IOrderProcessor. No entanto, sua implementação não é processar ordens. O serviço de mensagem de inatividade é um serviço de cliente e não tem a facilidade de processar ordens.

Observação

A fila de mensagens de inatividade é uma fila de cliente e é local para o gerenciador de filas do cliente.

A implementação do serviço de mensagem de inatividade 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 código de exemplo 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 de inatividade são mensagens endereçadas ao serviço que está processando a mensagem. Portanto, quando o serviço de mensagem de inatividade lê mensagens da fila, a camada do canal do Windows Communication Foundation (WCF) localiza a incompatibilidade em pontos de extremidade e não envia a mensagem. Nesse caso, a mensagem é endereçada ao serviço de processamento de ordens, mas é recebida pelo serviço de mensagem de inatividade. 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 mensagens com sucesso que são lidas da fila de inatividade.

Nesta amostra, o serviço de mensagem de inatividade reenvia a mensagem se o motivo da falha for que a mensagem atingiu o tempo limite. Por todos os outros motivos, ele exibe a falha de entrega, conforme 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();
        }
    }
}

A amostra a seguir mostra a configuração de uma mensagem de inatividade:

<?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 a amostra, há 3 executáveis a serem executados para ver como a fila de mensagens de inatividade funciona para cada aplicativo; o cliente, o serviço e um serviço de mensagens de inatividade que lê da fila de mensagens de inatividade para cada aplicativo e reenvia a mensagem para o serviço. Todos são aplicativos de console com saída nas janelas do console.

Observação

Como a fila está em uso, o cliente e o serviço não precisam estar em funcionamento ao mesmo tempo. Você pode executar o cliente, desligá-lo e iniciar o serviço e ele ainda receber as 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 de inatividade para cada aplicativo.

Em seguida, você executa o serviço de mensagens de inatividade, que lê a mensagem e exibe o código de erro e reenvia a mensagem de volta 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 a processa.

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 única 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 do 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 em Filas de Mensagens Privadas e selecione NovaFila Privada.

    4. Marque a caixa Transacional.

    5. Insira ServiceModelSamplesTransacted como o nome da nova fila.

  3. Para compilar a edição .NET do C# ou do Visual Basic da solução, siga as instruções em Como compilar as amostras do Windows Communication Foundation.

  4. Para executar a amostra em configuração de computador único ou entre computadores, altere os nomes da fila adequadamente, substituindo localhost pelo nome completo do computador e siga as instruções em Executando as amostras do Windows Communication Foundation.

Para executar a amostra em um computador ingressado em um grupo de trabalho

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

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

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

  2. Verifique de alterar a configuração no DeadLetterService, no servidor e no cliente antes de executar a amostra.

    Observação

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

Comentários

Por padrão com o transporte de associação netMsmqBinding, a segurança é habilitada. Duas propriedades, MsmqAuthenticationMode e MsmqProtectionLevel, determinam juntas 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 esta amostra em um computador que não faz parte de um domínio, você receberá o seguinte erro: "O certificado de enfileiramento de mensagem interna do usuário não existe".