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
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
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.
Abra o Gerenciador do Servidor no Visual Studio 2012.
Expanda a guia Recursos.
Clique com o botão direito do mouse em Filas de Mensagens Privadas e selecione NovaFila Privada.
Marque a caixa Transacional.
Insira
ServiceModelSamplesTransacted
como o nome da nova fila.
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.
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
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.
Certifique-se de alterar a configuração no PoisonMessageServer, servidor e cliente antes de executar o exemplo.
Observação
Definir
security mode
comoNone
é equivalente a configurar a segurançaMsmqAuthenticationMode
,MsmqProtectionLevel
eMessage
comoNone
.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
.