Condividi tramite


Gestione dei messaggi avvelenati

Un messaggio avvelenato è un messaggio che ha superato il numero massimo di tentativi di recapito all'applicazione. Questa situazione può verificarsi quando un'applicazione basata su coda non può elaborare un messaggio a causa di errori. Per soddisfare le esigenze di affidabilità, un'applicazione in coda riceve messaggi in una transazione. L'annullamento della transazione in cui è stato ricevuto un messaggio in coda lascia il messaggio nella coda in modo che il messaggio venga riprovato in una nuova transazione. Se il problema che ha causato l'annullamento della transazione non viene corretto, l'applicazione ricevente può rimanere bloccata in un ciclo di ricezione e annullamento dello stesso messaggio fino a quando non viene superato il numero massimo di tentativi di recapito, risultando in un messaggio velenoso.

Un messaggio può diventare un messaggio velenoso per molti motivi. I motivi più comuni sono specifici dell'applicazione. Ad esempio, se un'applicazione legge un messaggio da una coda ed esegue un'elaborazione del database, l'applicazione potrebbe non riuscire a ottenere un blocco nel database, causando l'interruzione della transazione. Poiché la transazione di database è stata interrotta, il messaggio rimane nella coda, causando la rilettura del messaggio da parte dell'applicazione una seconda volta e un altro tentativo di acquisizione di un blocco nel database. I messaggi possono diventare velenosi se contengono informazioni non valide. Ad esempio, un ordine di acquisto può contenere un numero di cliente non valido. In questi casi, l'applicazione può interrompere volontariamente la transazione e forzare il messaggio a diventare un messaggio velenoso.

In rari casi, i messaggi non possono essere inviati all'applicazione. Il livello Windows Communication Foundation (WCF) potrebbe riscontrare un problema con il messaggio, ad esempio se il messaggio presenta il frame errato, le credenziali del messaggio non valide associate o un'intestazione di azione non valida. In questi casi, l'applicazione non riceve mai il messaggio; tuttavia, il messaggio può comunque diventare un messaggio avvelenato ed essere elaborato manualmente.

Gestione dei messaggi avvelenati

In WCF, la gestione dei messaggi avvelenati fornisce un meccanismo che consente a un'applicazione ricevente di gestire i messaggi che non possono essere inviati all'applicazione o i messaggi inviati all'applicazione ma che non riescono ad essere elaborati a causa di motivi specifici dell'applicazione. Configurare la gestione dei messaggi avvelenati con le proprietà seguenti in ciascuna delle associazioni di coda disponibili.

  • ReceiveRetryCount. Valore intero che indica il numero massimo di tentativi di recapito di un messaggio dalla coda dell'applicazione all'applicazione. Il valore predefinito è 5. Ciò è sufficiente nei casi in cui un nuovo tentativo immediato risolve il problema, ad esempio con un deadlock temporaneo in un database.

  • MaxRetryCycles. Valore intero che indica il numero massimo di cicli di ripetizione dei tentativi. Un ciclo di ripetizione consiste nel trasferire un messaggio dalla coda dell'applicazione alla sotto-coda di ripetizione e, dopo un ritardo configurabile, dalla sotto-coda di ripetizione nuovamente nella coda dell'applicazione per tentare nuovamente la consegna. Il valore predefinito è 2. In Windows Vista il messaggio viene provato al massimo (ReceiveRetryCount +1) * (MaxRetryCycles + 1) volte. MaxRetryCycles viene ignorato in Windows Server 2003 e Windows XP.

  • RetryCycleDelay. Ritardo di tempo tra i cicli di ripetizione dei tentativi. Il valore predefinito è 30 minuti. MaxRetryCycles e RetryCycleDelay insieme forniscono un meccanismo per risolvere il problema in cui un nuovo tentativo dopo un ritardo periodico risolve il problema. Ad esempio, questo gestisce un set di righe bloccato in una transazione in sospeso in attesa di commit di SQL Server.

  • ReceiveErrorHandling. Enumerazione che indica l'azione da eseguire per un messaggio che non è stato consegnato dopo aver raggiunto il numero massimo di tentativi. I valori possono essere Fault, Drop, Reject e Move. L'opzione predefinita è Fault.

  • Colpa. Questa opzione invia un errore al listener che ha causato il guasto del ServiceHost. Il messaggio deve essere rimosso dalla coda dell'applicazione da un meccanismo esterno prima che l'applicazione possa continuare a elaborare i messaggi dalla coda.

  • Goccia. Questa opzione elimina il messaggio velenoso e questo messaggio non viene mai recapitato all'applicazione. Se a questo punto la proprietà TimeToLive del messaggio è scaduta, il messaggio potrebbe apparire nella coda dei messaggi non recapitabili del mittente. In caso contrario, il messaggio non viene visualizzato da nessuna parte. Questa opzione indica che l'utente non ha specificato cosa fare se il messaggio viene perso.

  • Rifiuta. Questa opzione è disponibile solo in Windows Vista. Ciò istruisce Microsoft Message Queuing (MSMQ) a inviare una conferma negativa al gestore della coda mittente, indicando che l'applicazione non può ricevere il messaggio. Il messaggio viene inserito nella coda dei messaggi non recapitati del gestore code di invio.

  • Sposta. Questa opzione è disponibile solo in Windows Vista. In questo modo il messaggio avvelenato viene spostato in una coda di messaggi avvelenati per un'elaborazione successiva da parte di un'applicazione di gestione dei messaggi avvelenati. La coda di messaggi non elaborabili è una coda secondaria della coda dell'applicazione. Un'applicazione per la gestione dei messaggi non elaborabili può essere un servizio WCF che legge i messaggi dalla coda dei messaggi non elaborabili. La coda non elaborabile è una coda secondaria della coda dell'applicazione e può essere indirizzata come net.msmq://<nome del computer>/applicationQueue; poison, dove nome del computer è il nome del computer in cui risiede la coda e il applicationQueue è il nome della coda specifica dell'applicazione.

Di seguito è riportato il numero massimo di tentativi di recapito eseguiti per un messaggio:

  • ((ReceiveRetryCount+1) * (MaxRetryCycles + 1)) su Windows Vista.

  • (ReceiveRetryCount + 1) su Windows Server 2003 e Windows XP.

Nota

Non vengono eseguiti nuovi tentativi per un messaggio recapitato correttamente.

Per tenere traccia del numero di tentativi di lettura di un messaggio, Windows Vista mantiene una proprietà di messaggio durevole che conta il numero di interruzioni e una proprietà di conteggio spostamenti che conta il numero di volte in cui il messaggio viene spostato tra la coda dell'applicazione e le sottoquery. Il canale WCF usa questi valori per calcolare il conteggio dei tentativi di ricezione e il conteggio dei cicli di ripetizione dei tentativi. In Windows Server 2003 e Windows XP il conteggio delle interruzioni viene mantenuto in memoria dal canale WCF e viene reimpostato se l'applicazione non riesce. Inoltre, il canale WCF può contenere i conteggi delle interruzioni per un massimo di 256 messaggi in memoria in qualsiasi momento. Se viene letto un 257° messaggio, viene reimpostato il numero di interruzioni del messaggio meno recente.

Il conteggio delle interruzioni e le proprietà del conteggio di spostamento sono disponibili per l'operazione del servizio nel contesto dell'operazione. Nell'esempio di codice seguente viene illustrato come accedervi.

MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;
Console.WriteLine($"Abort count: {mqProp.AbortCount} ");
Console.WriteLine($"Move count: {mqProp.MoveCount} ");
// code to submit purchase order ...

WCF offre due collegamenti standard in coda.

  • NetMsmqBinding. Associazione .NET Framework adatta per l'esecuzione di comunicazioni basate su coda con altri endpoint WCF.

  • MsmqIntegrationBinding. Metodo di associazione adatto per la comunicazione con le applicazioni di Accodamento Messaggi esistenti.

Nota

È possibile modificare le proprietà in queste associazioni in base ai requisiti del servizio WCF. L'intero meccanismo di gestione dei messaggi avvelenati è interno all'applicazione ricevente. Il processo è invisibile all'applicazione di invio, a meno che l'applicazione ricevente non si arresti e invii un riconoscimento negativo al mittente. In tal caso, il messaggio viene spostato nella coda dei messaggi non recapitabili del mittente.

Procedura consigliata: Gestione di MsmqPoisonMessageException

Quando il servizio determina che un messaggio è non elaborabile, il trasporto accodato genera un MsmqPoisonMessageException che contiene il LookupId del messaggio non elaborabile.

Un'applicazione ricevente può implementare l'interfaccia IErrorHandler per gestire eventuali errori richiesti dall'applicazione. Per altre informazioni, vedere l'estensione del controllo sulla gestione degli errori e la creazione di report.

L'applicazione potrebbe richiedere una gestione automatizzata dei messaggi non elaborabili che sposta i messaggi non elaborabili in una coda di messaggi non elaborabili in modo che il servizio possa accedere al resto dei messaggi nella coda. L'unico scenario per l'uso del meccanismo del gestore errori per monitorare le eccezioni di messaggi non elaborabili è quando l'impostazione ReceiveErrorHandling è su Fault. L'esempio di messaggio dannoso per Message Queuing 3.0 illustra questo comportamento. Di seguito sono descritti i passaggi da eseguire per gestire i messaggi velenosi, comprese le migliori pratiche:

  1. Assicurati che le impostazioni di avvelenamento riflettano i requisiti dell'applicazione. Quando si utilizzano le impostazioni, assicurarsi di comprendere le differenze tra le funzionalità di Accodamento messaggi in Windows Vista, Windows Server 2003 e Windows XP.

  2. Se necessario, implementare il IErrorHandler per gestire gli errori dei messaggi velenosi. Poiché l'impostazione di ReceiveErrorHandling su Fault richiede un meccanismo manuale per spostare il messaggio di errore dalla coda o per correggere un problema esterno, solitamente si implementa IErrorHandler quando ReceiveErrorHandling è impostato su Fault, come illustrato nel codice seguente.

    class PoisonErrorHandler : IErrorHandler
    {
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            // No-op -We are not interested in this. This is only useful if you want to send back a fault on the wire…not applicable for queues [one-way].
        }
    
        public bool HandleError(Exception error)
        {
            if (error != null && error.GetType() == typeof(MsmqPoisonMessageException))
            {
                Console.WriteLine($" Poisoned message -message look up id = {((MsmqPoisonMessageException)error).MessageLookupId}");
                return true;
            }
    
            return false;
        }
    }
    
  3. Creare un PoisonBehaviorAttribute da poter essere utilizzato dal comportamento del servizio. Il comportamento installa il IErrorHandler nel dispatcher. Vedere l'esempio di codice seguente.

    public class PoisonErrorBehaviorAttribute : Attribute, IServiceBehavior
    {
        Type errorHandlerType;
    
        public PoisonErrorBehaviorAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }
    
        void IServiceBehavior.Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
        }
    
        void IServiceBehavior.AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
        {
        }
    
        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
    
            try
            {
                errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
            }
            catch (MissingMethodException e)
            {
                throw new ArgumentException("The errorHandlerType specified in the PoisonErrorBehaviorAttribute constructor must have a public empty constructor", e);
            }
            catch (InvalidCastException e)
            {
                throw new ArgumentException("The errorHandlerType specified in the PoisonErrorBehaviorAttribute constructor must implement System.ServiceModel.Dispatcher.IErrorHandler", e);
            }
    
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }
    
  4. Assicurati che il tuo servizio sia annotato con l'attributo di comportamento di errore.

Inoltre, se il ReceiveErrorHandling è impostato su Fault, ServiceHost presenta errori al verificarsi del messaggio velenoso. È possibile collegarsi all'evento di errore per arrestare il servizio, eseguire azioni correttive e riavviare. Ad esempio, la LookupId nella MsmqPoisonMessageException propagata al IErrorHandler può essere rilevata, e in caso di errore dell'host del servizio, è possibile utilizzare l'API System.Messaging per ricevere il messaggio dalla coda e impiegare il LookupId per rimuoverlo e archiviare il messaggio in un archivio esterno o in un'altra coda. È quindi possibile riavviare ServiceHost per riprendere l'elaborazione normale. Il gestione dei messaggi non elaborabili in MSMQ 4.0 illustra questo comportamento.

Transazione Time-Out e messaggi avvelenati

Una classe di errori può verificarsi tra il canale di trasporto in coda e il codice utente. Questi errori possono essere rilevati da livelli tra livelli, ad esempio il livello di sicurezza dei messaggi o la logica di invio del servizio. Ad esempio, un certificato X.509 mancante rilevato nel livello di sicurezza SOAP e un'azione mancante sono casi in cui il messaggio viene inviato all'applicazione. In questo caso, il modello di servizio elimina il messaggio. Poiché il messaggio viene letto in una transazione e non è possibile specificare un risultato per tale transazione, alla fine la transazione raggiunge il timeout, viene annullata e il messaggio viene inserito nella coda. In altre parole, per una determinata classe di errori, la transazione non interrompe immediatamente, ma attende fino al timeout della transazione. È possibile modificare il timeout della transazione per un servizio usando ServiceBehaviorAttribute.

Per modificare il timeout della transazione a livello di computer, modificare il file machine.config e impostare il timeout della transazione appropriato. È importante notare che, a seconda del timeout impostato nella transazione, la transazione alla fine interrompe e torna alla coda e il relativo conteggio delle interruzioni viene incrementato. Alla fine, il messaggio diventa dannoso e la corretta disposizione viene effettuata in base alle impostazioni utente.

Sessioni e messaggi avvelenati

Una sessione viene sottoposta alle stesse procedure di ripetizione e gestione dei messaggi non elaborabili di un singolo messaggio. Le proprietà elencate in precedenza per i messaggi avvelenati si applicano all'intera sessione. Ciò significa che l'intera sessione viene ritentata e passa a una coda finale di messaggi non elaborabili o alla coda di messaggi non recapitabili del mittente se il messaggio viene rifiutato.

Invio in batch e messaggi non elaborabili

Se un messaggio diventa un messaggio velenoso e fa parte di un batch, l'intero batch viene annullato e il canale torna a leggere un messaggio alla volta. Per ulteriori informazioni sul raggruppamento in batch, vedere Messaggi in batch in una transazione

Gestione dei messaggi non elaborabili per i messaggi in una coda di messaggi problematici

La gestione dei messaggi non elaborabili non termina quando un messaggio viene inserito nella coda dei messaggi non elaborabili. I messaggi nella coda dei messaggi avvelenati devono comunque essere letti e gestiti. È possibile usare un subset delle impostazioni di gestione dei messaggi non elaborabili durante la lettura dei messaggi dalla sottoquery non elaborabile finale. Le impostazioni applicabili sono ReceiveRetryCount e ReceiveErrorHandling. È possibile impostare ReceiveErrorHandling su Drop, Reject o Fault. MaxRetryCycles viene ignorata e viene generata un'eccezione se ReceiveErrorHandling è impostato su 'Move'.

Differenze di Windows Vista, Windows Server 2003 e Windows XP

Come indicato in precedenza, non tutte le impostazioni di gestione dei messaggi avvelenati si applicano a Windows Server 2003 e Windows XP. Le seguenti differenze chiave tra Accodamento Messaggi in Windows Server 2003, Windows XP e Windows Vista sono rilevanti per la gestione dei messaggi velenosi:

  • Accodamento messaggi in Windows Vista supporta le sottoquery, mentre Windows Server 2003 e Windows XP non supportano le sottoquery. Le sottocode vengono usate nella gestione dei messaggi avvelenati. Le code di ripetizione dei tentativi e la coda dei messaggi avvelenati sono sotto-code alla coda dell'applicazione creata in base alle impostazioni di gestione dei messaggi avvelenati. Il MaxRetryCycles determina il numero di sottoquery da creare. Pertanto, quando viene eseguito in Windows Server 2003 o Windows XP, MaxRetryCycles vengono ignorati e ReceiveErrorHandling.Move non è consentito.

  • La messaggistica di accodamento in Windows Vista supporta il riconoscimento negativo, mentre Windows Server 2003 e Windows XP non lo supportano. Un riconoscimento negativo dal gestore delle code di ricezione fa sì che il gestore delle code di invio inserisca il messaggio rifiutato nella coda dei messaggi non recapitabili. Di conseguenza, ReceiveErrorHandling.Reject non è consentito con Windows Server 2003 e Windows XP.

  • Accodamento dei messaggi in Windows Vista supporta una proprietà che mantiene il conteggio dei tentativi di recapito. Questa proprietà di conteggio degli aborti non è disponibile in Windows Server 2003 e Windows XP. WCF mantiene il numero di interruzioni in memoria, pertanto è possibile che questa proprietà non contenga un valore accurato quando lo stesso messaggio viene letto da più di un servizio WCF in una farm.

Vedere anche