Compartilhar via


Tratamento de mensagens suspeitas no MSMQ 4.0

O exemplo MSMQ4 demonstra como realizar o tratamento de mensagens suspeitas em um serviço. Este exemplo se baseia no exemplo de Associação transacionada do MSMQ. Este exemplo usa netMsmqBinding. O serviço é um aplicativo de console auto-hospedado para permitir que você observe o serviço que está recebendo mensagens na fila.

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.

Uma mensagem suspeita é uma mensagem lida repetidamente de uma fila quando o serviço que está lendo a mensagem não pode processá-la e, portanto, encerra a transação sob a qual a mensagem é lida. Nesses casos, a mensagem recebe uma nova tentativa. Teoricamente, isso pode durar para sempre se houver um problema com a mensagem. Isso só pode ocorrer quando você usa transações para ler da fila e chamar a operação de serviço.

Com base na versão do MSMQ, o NetMsmqBinding dá suporte à detecção limitada à detecção completa de mensagens suspeitas. Depois que a mensagem for detectada como suspeitas, ela poderá ser tratada de várias maneiras. Novamente, com base na versão do MSMQ, o NetMsmqBinding dá suporte ao tratamento limitado ao tratamento completo de mensagens suspeitas.

Este exemplo ilustra as instalações limitadas de suspeita fornecidas na plataforma Windows Server 2003 e Windows XP e as instalações de suspeitas completas fornecidas no Windows Vista. Em ambos os exemplos, o objetivo é mover a mensagem suspeita da fila para outra fila. Essa fila pode então ser atendida por um serviço de mensagens suspeitas.

Exemplo de manipulação de suspeitas do MSMQ v4.0

No Windows Vista, o MSMQ fornece um recurso de subfila suspeito que pode ser usado para armazenar mensagens suspeitas. Este exemplo demonstra a prática recomendada para lidar com mensagens suspeitas usando o Windows Vista.

A detecção de mensagens suspeitas no Windows Vista é sofisticada. Há três propriedades que ajudam na detecção. A ReceiveRetryCount é o número de vezes que uma determinada mensagem é relida da fila e enviada ao aplicativo para processamento. Uma mensagem é relida da fila quando é recolocada na fila porque não é possível enviar a mensagem para o aplicativo ou o aplicativo reverte a transação na operação de serviço. MaxRetryCycles é o número de vezes que a mensagem é movida para a fila de repetição. Quando ReceiveRetryCount é atingido, a mensagem é movida para a fila de repetição. A propriedade RetryCycleDelay é o tempo de atraso após o qual a mensagem é movida da fila de repetição de volta para a fila principal. ReceiveRetryCount é redefinido para 0. Há uma nova tentativa para a mensagem. Se todas as tentativas de ler a mensagem falharem, a mensagem será marcada como suspeita.

Depois que a mensagem é marcada como suspeita, ela é tratada de acordo com as configurações na enumeração ReceiveErrorHandling. Para reiterar os valores possíveis:

  • Falha (padrão): falha do ouvinte e também do host de serviço.

  • Descartar: Para descartar a mensagem.

  • Mover: para mover a mensagem para a subfila da mensagem suspeita. Esse valor só está disponível no Windows Vista.

  • Rejeitar: Para rejeitar a mensagem, enviando a mensagem de volta para a fila de mensagens mortas do remetente. Esse valor só está disponível no Windows Vista.

O exemplo demonstra o uso da disposição Move para a mensagem suspeita. Move faz com que a mensagem seja movida para a subfila suspeita.

O contrato de serviço é IOrderProcessor, que define um serviço unidirecional adequado para uso com filas.

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

A operação de serviço exibe uma mensagem informando que está processando o pedido. Para demonstrar a funcionalidade de mensagem suspeita, a operação de serviço SubmitPurchaseOrder lança uma exceção para reverter a transação em uma invocação aleatória do serviço. Isso faz com que a mensagem seja colocada de volta na fila. Eventualmente, a mensagem é marcada como suspeita. A configuração é definida para mover a mensagem suspeita para a subfila suspeita.

// Service class that implements the service contract.
// Added code to write output to the console window.
public class OrderProcessorService : IOrderProcessor
{
    static Random r = new Random(137);

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {

        int randomNumber = r.Next(10);

        if (randomNumber % 2 == 0)
        {
            Orders.Add(po);
            Console.WriteLine("Processing {0} ", po);
        }
        else
        {
            Console.WriteLine("Aborting transaction, cannot process purchase order: " + po.PONumber);
            Console.WriteLine();
            throw new Exception("Cannot process purchase order: " + po.PONumber);
        }
    }

    public static void OnServiceFaulted(object sender, EventArgs e)
    {
        Console.WriteLine("Service Faulted");
    }

    // Host the service within this EXE console application.
    public static void Main()
    {
        // Get MSMQ queue name from app settings in configuration.
        string queueName = ConfigurationManager.AppSettings["queueName"];

        // Create the transacted MSMQ queue if necessary.
        if (!System.Messaging.MessageQueue.Exists(queueName))
            System.Messaging.MessageQueue.Create(queueName, true);

        // Get the base address that is used to listen for WS-MetaDataExchange requests.
        // This is useful to generate a proxy for the client.
        string baseAddress = ConfigurationManager.AppSettings["baseAddress"];

        // Create a ServiceHost for the OrderProcessorService type.
        ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService), new Uri(baseAddress));

        // Hook on to the service host faulted events.
        serviceHost.Faulted += new EventHandler(OnServiceFaulted);

        // Open the ServiceHostBase to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        if(serviceHost.State != CommunicationState.Faulted) {
            serviceHost.Close();
        }

    }
}

A configuração do serviço inclui as seguintes propriedades de mensagem suspeita: receiveRetryCount, maxRetryCycles, retryCycleDelay e receiveErrorHandling, conforme mostrado no arquivo de configuração a seguir.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- Use appSetting to configure MSMQ queue name. -->
    <add key="queueName" value=".\private$\ServiceModelSamplesPoison" />
    <add key="baseAddress" value="http://localhost:8000/orderProcessor/poisonSample"/>
  </appSettings>
  <system.serviceModel>
    <services>
      <service
              name="Microsoft.ServiceModel.Samples.OrderProcessorService">
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesPoison"
                  binding="netMsmqBinding"
                  bindingConfiguration="PoisonBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
      </service>
    </services>

    <bindings>
      <netMsmqBinding>
        <binding name="PoisonBinding"
                 receiveRetryCount="0"
                 maxRetryCycles="1"
                 retryCycleDelay="00:00:05"
                 receiveErrorHandling="Move">
        </binding>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Processando mensagens da fila de mensagens suspeitas

O serviço de mensagens suspeitas lê as mensagens da fila final de mensagens suspeitas e as processa.

As mensagens na fila de mensagens suspeitas são mensagens endereçadas ao serviço que está processando a mensagem, que pode ser diferente do ponto de extremidade do serviço de mensagens suspeitas. Portanto, quando o serviço de mensagens suspeitas lê mensagens da fila, a camada de canal WCF encontra a incompatibilidade nos terminais e não despacha a mensagem. Nesse caso, a mensagem é endereçada ao serviço de processamento de pedidos, mas está sendo recebida pelo serviço de mensagens suspeitas. Para continuar a receber a mensagem, mesmo que a mensagem seja endereçada a um ponto de extremidade diferente, devemos adicionar um ServiceBehavior para filtrar endereços em que o critério de correspondência seja corresponder a qualquer ponto de extremidade de serviço ao qual a mensagem seja endereçada. Isso é necessário para processar com sucesso as mensagens que você lê na fila de mensagens suspeitas.

A própria implementação do serviço de mensagens suspeitas é muito semelhante à implementação do serviço. Implementa o contrato e processa os pedidos. O exemplo de código é o seguinte.

// Service class that implements the service contract.
// Added code to write output to the console window.
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
public class OrderProcessorService : IOrderProcessor
{
    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {
        Orders.Add(po);
        Console.WriteLine("Processing {0} ", po);
    }

    public static void OnServiceFaulted(object sender, EventArgs e)
    {
        Console.WriteLine("Service Faulted...exiting app");
        Environment.Exit(1);
    }

    // Host the service within this EXE console application.
    public static void Main()
    {

        // Create a ServiceHost for the OrderProcessorService type.
        ServiceHost serviceHost = new ServiceHost(typeof(OrderProcessorService));

        // Hook on to the service host faulted events.
        serviceHost.Faulted += new EventHandler(OnServiceFaulted);

        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The poison message service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHostBase to shutdown the service.
        if(serviceHost.State != CommunicationState.Faulted)
        {
            serviceHost.Close();
        }
    }

Ao contrário do serviço de processamento de pedidos que lê mensagens da fila de pedidos, o serviço de mensagens suspeitas lê mensagens da subfila suspeita. A fila suspeita é uma subfila da fila principal, é denominada "suspeita" e é gerada automaticamente pelo MSMQ. Para acessá-la, forneça o nome da fila principal seguido por um ";" e o nome da subfila, neste caso -"suspeita", conforme mostrado na configuração de exemplo a seguir.

Observação

No exemplo do MSMQ v3.0, o nome da fila suspeita não é uma subfila, mas a fila para a qual movemos a mensagem.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Microsoft.ServiceModel.Samples.OrderProcessorService">
        <!-- Define NetMsmqEndpoint -->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesPoison;poison"
                  binding="netMsmqBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" >
        </endpoint>
      </service>
    </services>

  </system.serviceModel>
</configuration>

Ao executar o exemplo, as atividades do cliente, do serviço e do serviço de mensagens suspeitas são exibidas nas janelas do console. Você pode ver o serviço receber mensagens do cliente. Pressione ENTER em cada janela do console para encerrar os serviços.

O serviço começa a ser executado, processando pedidos e aleatoriamente começa a encerrar o processamento. Se a mensagem indicar que o pedido foi processado, você poderá executar o cliente novamente para enviar outra mensagem até ver que o serviço realmente encerrou uma mensagem. Com base nas configurações de suspeita configuradas, a mensagem recebe uma tentativa para processamento antes de movê-la para a fila final de suspeita.

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

Processing Purchase Order: 0f063b71-93e0-42a1-aa3b-bca6c7a89546
        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

Processing Purchase Order: 5ef9a4fa-5a30-4175-b455-2fb1396095fa
        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

Aborting transaction, cannot process purchase order: 23e0b991-fbf9-4438-a0e2-20adf93a4f89

Inicie o serviço de mensagens suspeitas para ler a mensagem suspeita da fila suspeita. Neste exemplo, o serviço de mensagens suspeitas lê a mensagem e a processa. Você pode ver que a ordem de compra que foi encerrada e marcada como suspeita é lida pelo serviço de mensagens suspeitas.

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

Processing Purchase Order: 23e0b991-fbf9-4438-a0e2-20adf93a4f89
        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 C# ou do Visual Basic .NET da solução, siga as instruções em Como compilar o exemplos do Windows Communication Foundation.

  4. Para executar o exemplo em uma configuração de computador único ou entre computadores, altere os nomes das filas para refletir o nome do host real em vez do host local e siga as instruções em Executando os exemplos do Windows Communication Foundation.

Por padrão, com o transporte de associação netMsmqBinding, a segurança é ativada. 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, receberá o seguinte erro: "O certificado de enfileiramento de mensagens interno do usuário não existe".

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

  1. Se o seu 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, conforme mostrado na configuração de exemplo a seguir:

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

    Certifique-se de que o ponto de extremidade esteja ligado à associação configurando o atributo bindingConfiguration do ponto de extremidade.

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

    Observação

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

  3. Para que o Meta Data Exchange funcione, registramos uma URL com associação http. Isso requer que o serviço seja executado em uma janela de comando elevada. Caso contrário, você obtém uma exceção como: Unhandled Exception: System.ServiceModel.AddressAccessDeniedException: HTTP could not register URL http://+:8000/ServiceModelSamples/service/. Your process does not have access rights to this namespace (see https://go.microsoft.com/fwlink/?LinkId=70353 for details). ---> System.Net.HttpListenerException: Access is denied.