Condividi tramite


Gestione dei messaggi non gestibili in MSMQ 4.0

L'esempio MSMQ4 illustra come eseguire la gestione dei messaggi dannosi in un servizio. Questo esempio si basa sull'esempio dell'associazione MSMQ transazionata. In questo esempio viene usato il netMsmqBinding. Il servizio è un'applicazione console autogestita che permette di osservare la ricezione di messaggi in coda da parte del servizio.

Nella comunicazione in coda, il client comunica con il servizio usando una coda. Più precisamente, il client invia messaggi a una coda. Il servizio riceve messaggi dalla coda. Non è quindi necessario che il servizio e il client siano in esecuzione contemporaneamente per comunicare tramite una coda.

Un messaggio velenoso è un messaggio che viene letto ripetutamente da una coda quando il servizio che legge il messaggio non può elaborarlo e quindi termina la transazione sotto cui viene letto il messaggio. In questi casi, il messaggio viene ritentato. Questo può teoricamente andare avanti per sempre se c'è un problema con il messaggio. Ciò può verificarsi solo quando si usano transazioni per leggere dalla coda ed eseguire l'operazione di servizio.

A seconda della versione di MSMQ, il NetMsmqBinding supporta dal rilevamento limitato al rilevamento completo dei messaggi velenosi. Dopo che il messaggio è stato rilevato come avvelenato, può essere gestito in diversi modi. Anche in questo caso, in base alla versione di MSMQ, NetMsmqBinding supporta una gestione che va da limitata a completa dei messaggi velenosi.

Questo esempio illustra i limitati meccanismi di gestione degli errori forniti nella piattaforma Windows Server 2003 e Windows XP e i meccanismi completi forniti in Windows Vista. In entrambi gli esempi, l'obiettivo è spostare il messaggio avvelenato dalla coda in un'altra coda. Tale coda può quindi essere gestita da un servizio di gestione dei messaggi problematici.

Esempio di gestione dei messaggi velenosi MSMQ v4.0

In Windows Vista, MSMQ fornisce una funzionalità di sottocoda dei messaggi velenosi che può essere usata per archiviare messaggi velenosi. In questo esempio viene illustrata la procedura consigliata per gestire i messaggi non elaborabili tramite Windows Vista.

Il rilevamento dei messaggi non elaborabili in Windows Vista è sofisticato. Esistono 3 proprietà che consentono di rilevare. ReceiveRetryCount è il numero di volte in cui un determinato messaggio viene riletto dalla coda e inviato all'applicazione per essere elaborato. Un messaggio viene rilette dalla coda quando viene reinserito nella coda perché il messaggio non può essere inviato all'applicazione o l'applicazione esegue il rollback della transazione nell'operazione del servizio. MaxRetryCycles è il numero di volte in cui il messaggio viene spostato nella coda di nuovi tentativi. Quando ReceiveRetryCount viene raggiunto, il messaggio viene spostato nella coda di ripetizione. La proprietà RetryCycleDelay è il ritardo temporale dopo il quale il messaggio viene spostato dalla coda di ripetizione dei tentativi alla coda principale. ReceiveRetryCount viene reimpostato su 0. Il messaggio viene riprovato. Se tutti i tentativi di lettura del messaggio sono falliti, il messaggio viene contrassegnato come avvelenato.

Quando il messaggio viene contrassegnato come avvelenato, il messaggio viene gestito in base alle impostazioni nell'enumerazione ReceiveErrorHandling. Per ribadire i possibili valori:

  • Errore (impostazione predefinita): per eseguire l'errore del listener e anche dell'host del servizio.

  • Drop: per eliminare il messaggio.

  • sposta: per spostare il messaggio nella sottocoda dei messaggi non elaborabili. Questo valore è disponibile solo in Windows Vista.

  • Rifiuta: per rifiutare il messaggio, inviare di nuovo il messaggio alla coda dei messaggi non recapitabili del mittente. Questo valore è disponibile solo in Windows Vista.

L'esempio dimostra l'uso della disposizione Move per il messaggio avvelenato. Move fa sì che il messaggio si sposti nella sottocoda avvelenata.

Il contratto di servizio è IOrderProcessor, che definisce un servizio unidirezionale adatto per l'uso con le code.

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

L'operazione del servizio visualizza un messaggio che indica che l'ordine è in fase di elaborazione. Per illustrare la funzionalità dei messaggi avvelenati, l'operazione SubmitPurchaseOrder del servizio genera un'eccezione per eseguire il rollback della transazione durante un'invocazione casuale del servizio. In questo modo, il messaggio viene rimesso nella coda. Infine il messaggio viene contrassegnato come velenoso. La configurazione è impostata per spostare il messaggio avvelenato nella sottocoda dei messaggi avvelenati.

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

    }
}

La configurazione del servizio include le proprietà del messaggio non elaborabili seguenti: receiveRetryCount, maxRetryCycles, retryCycleDelaye receiveErrorHandling come illustrato nel file di configurazione seguente.

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

Elaborazione dei messaggi dalla coda di messaggi non elaborabili

Il servizio di messaggi non elaborabili legge i messaggi dalla coda di messaggi non elaborabili finale e li elabora.

I messaggi nella coda dei messaggi non elaborabili sono indirizzati al servizio che processa il messaggio, e questo potrebbe differire dall'endpoint del servizio stesso dei messaggi non elaborabili. Pertanto, quando il servizio di messaggi non elaborabili legge i messaggi dalla coda, il livello del canale WCF rileva una discrepanza negli endpoint e non distribuisce il messaggio. In questo caso, il messaggio viene indirizzato al servizio di elaborazione degli ordini, ma viene ricevuto dal servizio di gestione dei messaggi avvelenati. Per continuare a ricevere il messaggio anche se il messaggio viene indirizzato a un endpoint diverso, è necessario aggiungere un ServiceBehavior oggetto per filtrare gli indirizzi in cui il criterio di corrispondenza corrisponde a qualsiasi endpoint di servizio a cui viene indirizzato il messaggio. Questa operazione è necessaria per elaborare correttamente i messaggi letti dalla coda di messaggi danneggiati.

L'implementazione del servizio messaggi non elaborabili è molto simile all'implementazione del servizio. Implementa il contratto ed elabora gli ordini. L'esempio di codice è il seguente.

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

A differenza del servizio di elaborazione degli ordini che legge i messaggi dalla coda degli ordini, il servizio messaggi non elaborabili legge i messaggi dalla sottocoda dei messaggi non elaborabili. La coda poison è una sottocoda della coda principale, denominata "poison" e viene generata automaticamente da MSMQ. Per accedervi, specificare il nome della coda principale seguito da un ";" e il nome della coda secondaria, in questo caso -"poison", come illustrato nella configurazione di esempio seguente.

Annotazioni

Nell'esempio per MSMQ v3.0, il nome della coda avvelenata non è una coda secondaria, ma è la coda a cui abbiamo spostato il messaggio.

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

Quando esegui l'esempio, le attività del client, del servizio e del servizio di messaggi non elaborabili vengono visualizzate nelle finestre della console. È possibile visualizzare il servizio che riceve messaggi dal client. Premere INVIO in ogni finestra della console per arrestare i servizi.

Il servizio inizia a funzionare, elabora gli ordini e in modo casuale inizia ad arrestare l'elaborazione. Se il messaggio indica che l'ordine è stato elaborato, è possibile eseguire di nuovo il client per inviare un altro messaggio fino a quando il servizio non ha effettivamente terminato un messaggio. In base alle impostazioni di errore configurate, il messaggio viene elaborato una volta prima di spostarlo nella coda finale dei messaggi di errore.

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

Avviare il servizio messaggi velenosi per leggere il messaggio velenoso dalla coda velenosa. In questo esempio, il servizio di gestione dei messaggi non elaborabili legge ed elabora il messaggio. Si può vedere che l'ordine di acquisto terminato e avvelenato viene letto dal servizio di messaggi avvelenati.

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

Per configurare, compilare ed eseguire l'esempio

  1. Assicurati di aver eseguito la procedura di installazione di One-Time per gli esempi di Windows Communication Foundation.

  2. Se il servizio viene eseguito per primo, verifica che la coda sia presente. Se la coda non è presente, il servizio ne creerà uno. È possibile eseguire prima il servizio per creare la coda oppure crearne una tramite Gestione code MSMQ. Seguire questa procedura per creare una coda in Windows 2008.

    1. Aprire Server Manager in Visual Studio 2012.

    2. Espandi la scheda funzionalità.

    3. Fare clic con il pulsante destro del mouse code di messaggi privati e selezionare Nuovo, coda privata.

    4. Selezionare la casella transazionale.

    5. Inserire ServiceModelSamplesTransacted come nome della nuova coda.

  3. Per compilare l'edizione C# o Visual Basic .NET della soluzione, seguire le istruzioni in Compilazione degli esempi di Windows Communication Foundation.

  4. Per eseguire l'esempio in una configurazione con computer singolo o incrociato, modificare i nomi della coda in modo da riflettere il nome host effettivo anziché localhost e seguire le istruzioni riportate in Esecuzione degli esempi di Windows Communication Foundation.

Per impostazione predefinita con il binding di trasporto netMsmqBinding, la sicurezza è abilitata. Due proprietà, MsmqAuthenticationMode e MsmqProtectionLevel, insieme determinano il tipo di sicurezza del trasporto. Per impostazione predefinita, la modalità di autenticazione è impostata su Windows e il livello di protezione è impostato su Sign. Affinché MSMQ fornisca la funzionalità di autenticazione e firma, deve far parte di un dominio. Se si esegue questo esempio in un computer che non fa parte di un dominio, viene visualizzato l'errore seguente: "Il certificato di accodamento messaggi interno dell'utente non esiste".

Per eseguire l'esempio in un computer aggiunto a un gruppo di lavoro

  1. Se il computer non fa parte di un dominio, disattivare la sicurezza del trasporto impostando la modalità di autenticazione e il livello di protezione su None come illustrato nella configurazione di esempio seguente:

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

    Verificare che l'endpoint sia associato all'associazione impostando l'attributo bindingConfiguration dell'endpoint.

  2. Assicurarsi di modificare la configurazione in PoisonMessageServer, nel server e nel client prima di eseguire l'esempio.

    Annotazioni

    Impostare security mode su None equivale a impostare la sicurezza di MsmqAuthenticationMode, MsmqProtectionLevel e Message su None.

  3. Per consentire il funzionamento di Meta Data Exchange, viene registrato un URL con l'associazione HTTP. Ciò richiede che il servizio sia eseguito in una finestra di comando con privilegi elevati. In caso contrario, si ottiene un'eccezione, ad esempio: 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.