Gestion des messages incohérents

Un message incohérent est un message qui a dépassé le nombre maximal de tentatives de remise à l’application. Cette situation peut survenir lorsqu'une application basée sur file d'attente ne peut pas traiter un message car des erreurs se sont produites. Pour faire face aux demandes de fiabilité, une application en file d’attente reçoit des messages sous une transaction. L’abandon de la transaction dans laquelle un message en file d’attente a été reçu laisse le message dans la file d’attente afin qu’une nouvelle tentative de remise puisse être effectuée sous une nouvelle transaction. Si le problème qui a provoqué l'abandon de la transaction n'est pas résolu, l'application réceptrice peut être bloquée dans une réception et un abandon en boucle du même message jusqu'à ce que le nombre maximal de tentatives de remise soit dépassé et qu'un message incohérent soit généré.

Un message peut devenir incohérent pour de nombreuses raisons. Les raisons les plus courantes sont spécifiques à l’application. Par exemple, si une application lit un message à partir d'une file d'attente et exécute un traitement de base de données, il est possible qu'elle ne parvienne pas à obtenir un verrou sur la base de données, provoquant ainsi l'abandon de la transaction. Parce que la transaction de base de données a été abandonnée, le message reste dans la file d'attente, ce qui conduit l'application à relire le message une deuxième fois et à tenter de nouveau d'acquérir un verrou sur la base de données. Les messages peuvent également devenir incohérents s'ils contiennent des informations non valides. Par exemple, une commande fournisseur peut contenir un numéro de client non valide. Dans ces cas-là, l’application peut abandonner volontairement la transaction et forcer le message à devenir un message incohérent.

Dans de rares occasions, des messages peuvent ne pas être distribués à l'application. La couche Windows Communication Foundation (WCF) peut détecter un problème au niveau du message, par exemple une trame incorrecte, des informations d’identification de message non valides jointes au message ou un en-tête d’action non valide. Dans ces cas-là, l'application ne reçoit jamais le message ; toutefois, ce dernier peut encore devenir un message incohérent et être traité manuellement.

Gestion des messages incohérents

Dans WCF, la gestion des messages incohérents fournit un mécanisme permettant à une application réceptrice de traiter les messages qui ne peuvent pas être distribués à l’application ou les messages distribués à l’application mais dont le traitement échoue pour des raisons spécifiques à l’application. Configurez la gestion des messages incohérents avec les propriétés suivantes dans chacune des liaisons en file d’attente disponibles :

  • ReceiveRetryCount. Valeur entière qui indique le nombre maximal de nouvelles tentatives de remise d'un message à partir de la file d'attente d'application à l'application. La valeur par défaut est 5. Elle est suffisante dans les cas où une nouvelle tentative immédiate résout le problème (par exemple, en cas d'interblocage temporaire sur une base de données).

  • MaxRetryCycles. Valeur entière qui indique le nombre maximal de cycles de nouvelle tentative. Un cycle de nouvelle tentative implique le transfert d'un message de la file d'attente d'application vers la sous-file d'attente de nouvel essai et, après un délai configurable, de la sous-file d'attente de nouvel essai vers la file d'attente d'application afin de retenter la remise. La valeur par défaut est 2. Sur Windows Vista, le nombre maximal de tentatives de remise du message est de (ReceiveRetryCount + 1) * (MaxRetryCycles + 1). MaxRetryCycles est ignoré sur Windows Server 2003 et Windows XP.

  • RetryCycleDelay. Délai entre les cycles de nouvelle tentative. La valeur par défaut est 30 minutes. MaxRetryCycles et RetryCycleDelay fournissent ensemble un mécanisme permettant de gérer un problème qui peut être résolu en cas de nouvelle tentative après un délai périodique. Par exemple, cela permet de gérer le verrouillage d’un ensemble de lignes dans la validation de transaction en attente SQL Server.

  • ReceiveErrorHandling. Énumération qui indique l'action à effectuer pour un message qui n'a pas pu être remis une fois le nombre maximal de tentatives effectué. Les valeurs peuvent être Fault, Drop, Reject et Move. L'option par défaut est Fault.

  • Fault. Cette option envoie une erreur à l'écouteur qui a provoqué l'échec du ServiceHost. Le message doit être supprimé de la file d'attente d'application par un mécanisme externe pour que l'application puisse continuer à traiter les messages de la file d'attente.

  • Drop. Cette option supprime le message empoisonné ; celui-ci n'est jamais remis à l'application. Si la propriété TimeToLive du message a expiré à ce stade, le message peut apparaître dans la file d'attente de lettres mortes de l'expéditeur. Sinon, il n'apparaît nulle part. Cette option indique que l'utilisateur n'a pas spécifié quoi faire en cas de perte du message.

  • Reject. Cette option est disponible uniquement sur Windows Vista. Elle fait en sorte que Message Queuing (MSMQ) renvoie un accusé de réception négatif au gestionnaire de files d’attente d’envoi, signalant que l’application ne peut pas recevoir le message. Le message est placé dans la file d'attente de lettres mortes du gestionnaire de files d'attente émetteur.

  • Move. Cette option est disponible uniquement sur Windows Vista. Elle déplace le message incohérent vers une file d'attente de messages incohérents pour un traitement ultérieur par une application de gestion de messages incohérents. La file d'attente de messages incohérents est une sous-file d'attente de la file d'attente d'application. Une application de gestion de messages incohérents peut être un service WCF qui lit des messages à partir de la file d’attente de messages incohérents. La file d’attente de messages incohérents est une sous-file d’attente de la file d’attente d’application ; elle peut être traitée en tant que net.msmq://<nom_machine>/file_attente_application;poison, où nom_machine est le nom de l’ordinateur sur lequel réside la file d’attente et file_attente_application est le nom de la file d’attente propre à l’application.

Voici le nombre maximal de tentatives de remise effectuées pour un message :

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

  • (ReceiveRetryCount + 1) sur Windows Server 2003 et Windows XP.

Notes

Aucune nouvelle tentative n'est effectuée pour un message remis avec succès.

Pour effectuer le suivi du nombre de tentatives de lecture d’un message, Windows Vista tient à jour une propriété de message durable qui compte le nombre d’abandons et une propriété de compte de déplacements qui compte le nombre de déplacements du message entre la file d’attente d’application et les sous-files d’attente. Le canal WCF utilise ces propriétés pour calculer le nombre de nouvelles tentatives de réception et le nombre de cycles de nouvelle tentative. Sur Windows Server 2003 et Windows XP, le nombre d’abandons est conservé en mémoire par le canal WCF et réinitialisé si l’application échoue. De plus, le canal WCF peut contenir à tout moment en mémoire le nombre d’abandons pour 256 messages. Si un 257ème message est lu, le nombre d'abandons du message le plus ancien est réinitialisé.

Les propriétés de nombre d'abandons et de nombre déplacements sont accessibles à l'opération de service par le biais du contexte d'opération. L'exemple de code suivant montre comment y accéder.

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

WCF fournit deux liaisons en file d’attente standard :

  • NetMsmqBinding. Une liaison .NET Framework adaptée à la communication basée sur une file d’attente avec d’autres points de terminaison WCF.

  • MsmqIntegrationBinding. Une liaison adaptée à la communication avec des applications Message Queuing existantes.

Notes

Vous pouvez modifier les propriétés dans ces liaisons en fonction des exigences de votre service WCF. L'ensemble du mécanisme de gestion de messages incohérents est local à l'application de réception. Le processus est invisible à l'application émettrice, à moins que l'application de réception ne s'arrête finalement et ne renvoie un accusé de réception négatif à l'expéditeur. Dans ce cas, le message est déplacé vers la file d'attente de lettres mortes de l'expéditeur.

Recommandation : Gestion de MsmqPoisonMessageException

Lorsque le service détermine qu'un message est incohérent, le transport de mise en file d'attente lève une MsmqPoisonMessageException qui contient le LookupId du message incohérent.

Une application de réception peut implémenter l'interface IErrorHandler pour gérer toute erreur requise par l'application. Pour plus d’informations, consultez Extension du contrôle à la gestion des erreurs et au rapport d’erreurs.

Il est possible que l'application requière un système de gestion automatisée des messages empoisonnés qui déplace ceux-ci vers une file d'attente de messages empoisonnés afin que le service puisse accéder au reste des messages dans la file d'attente. Le seul scénario dans lequel on utilise le mécanisme de gestionnaire d'erreurs pour écouter les exceptions de message incohérent est lorsque le paramètre ReceiveErrorHandling a la valeur Fault. L'exemple de message empoisonné pour Message Queuing 3.0 illustre ce comportement. La section suivante décrit les étapes à suivre pour gérer des messages incohérents et fournit quelques recommandations :

  1. Assurez-vous que vos paramètres de messages empoisonnés reflètent les besoins de votre application. Lors de l’utilisation des paramètres, assurez-vous de bien comprendre les différences entre les fonctionnalité de Message Queuing sur Windows Vista, Windows Server 2003 et Windows XP.

  2. Si nécessaire, implémentez le IErrorHandler pour gérer les erreurs de message incohérent. Étant donné que l'affectation de la valeur ReceiveErrorHandling à Fault nécessite un mécanisme manuel pour déplacer le message empoisonné hors de la file d'attente ou pour corriger un problème dépendant externe, l'utilisation typique consiste à implémenter IErrorHandler lorsque ReceiveErrorHandling a la valeur Fault, comme illustré dans le code suivant.

    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 = {0}", ((MsmqPoisonMessageException)error).MessageLookupId);
                return true;
            }
    
            return false;
        }
    }
    
  3. Créez un PoisonBehaviorAttribute que le comportement de service peut utiliser. Le comportement installe le IErrorHandler sur le répartiteur. Voir l'exemple de code suivant.

    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. Assurez-vous que votre service est annoté avec l’attribut de comportement d’incohérence.

De plus, si ReceiveErrorHandling a la valeur Fault, ServiceHost renverra une erreur s'il rencontre le message empoisonné. Vous pouvez vous raccorder à l’événement d’erreur et arrêter le service, appliquer des actions correctives, puis redémarrer. Par exemple, LookupId dans le MsmqPoisonMessageException propagé au IErrorHandler peut être noté, et lorsque l'hôte de service renvoie une erreur, vous pouvez utiliser l'API System.Messaging pour recevoir le message de la file d'attente à l'aide de LookupId, supprimer le message de la file d'attente, puis stocker le message dans un magasin externe ou une autre file d'attente. Vous pouvez ensuite redémarrer ServiceHost pour continuer le traitement normal. La Gestion des messages incohérents dans MSMQ 4.0 illustre ce comportement.

Délai d'expiration de transaction et messages incohérents

Une classe d'erreurs peut se produire entre le canal de transport de mise en file d'attente et le code utilisateur. Ces erreurs peuvent être détectées par des couches intermédiaires, telles que la couche de sécurité de message ou la logique de distribution de service. Par exemple, un certificat X.509 manquant détecté dans la couche de sécurité SOAP et une action manquante sont des cas où le message est distribué à l'application. Lorsque cela se produit, le modèle de service dépose le message. Étant donné que le message est lu dans une transaction et qu’aucun résultat pour cette transaction ne peut être fourni, le délai d’attente de transaction finit par expirer, la transaction est abandonnée et le message est remis dans la file d’attente. En d’autres termes, pour une certaine classe d’erreurs, la transaction n’abandonne pas immédiatement mais attend la fin de son délai d’expiration. Vous pouvez modifier le délai d’expiration de transaction pour un service à l’aide de ServiceBehaviorAttribute.

Pour modifier le délai d’expiration de transaction à l’échelle de l’ordinateur, modifiez le fichier machine.config et définissez le délai d’expiration de transaction approprié. Il est important de noter que, en fonction du délai d’expiration défini dans la transaction, cette dernière finit par abandonner, revient dans la file d’attente et son compteur d’abandons est incrémenté. Pour finir, le message devient empoisonné et la disposition correcte est effectuée conformément aux paramètres utilisateur.

Sessions et messages incohérents

Une session subit les mêmes procédures de nouvelle tentative et de gestion des messages incohérents qu'un simple message. Les propriétés indiquées précédemment pour les messages empoisonnés s'appliquent à la session entière. En d'autres termes, la session entière est soumise à une nouvelle tentative et finit dans une ultime file d'attente de messages empoisonnés ou dans la file d'attente de lettres mortes de l'expéditeur si le message est refusé.

Traitement par lot et messages incohérents

Si un message faisant partie d'un lot devient un message incohérent, le lot entier est annulé et le canal recommence à lire un message à la fois. Pour plus d’informations sur le traitement par lots, consultez Traitement par lots des messages dans une transaction

Gestion des messages incohérents pour les messages dans une file d'attente de messages incohérents

La gestion des messages incohérents ne se termine pas lorsqu'un message est placé dans la file d'attente de messages incohérents. Les messages dans la file d'attente de messages incohérents doivent encore être lus et gérés. Vous pouvez utiliser un sous-ensemble des paramètres de gestion de messages incohérents lors de la lecture des messages à partir de l'ultime sous-file d'attente de messages incohérents. Les paramètres applicables sont ReceiveRetryCount et ReceiveErrorHandling. Vous pouvez affecter la valeur Drop, Reject ou Fault à ReceiveErrorHandling. MaxRetryCycles est ignoré et une exception est levée si ReceiveErrorHandling a la valeur Move.

Différences entre Windows Vista, Windows Server 2003 et Windows XP

Comme noté précédemment, tous les paramètres de gestion de messages incohérents ne s’appliquent pas à Windows Server 2003 et Windows XP. Les principales différences suivantes entre Message Queuing sur Windows Server 2003, Windows XP et Windows Vista concernent la gestion des messages incohérents :

  • Message Queuing dans Windows Vista prend en charge les sous-files d’attente, alors que Windows Server 2003 et Windows XP ne le font pas. Les sous-files d'attente sont utilisées dans la gestion des messages incohérents. Les files d'attente de nouvel essai et la file d'attente de messages incohérents sont des sous-files d'attente de la file d'attente de l'application créée en fonction des paramètres de gestion des messages incohérents. MaxRetryCycles définit le nombre de sous-files de nouvel essai à créer. Par conséquent, lors de l’exécution sur Windows Server 2003 ou Windows XP, les valeurs MaxRetryCycles sont ignorées et ReceiveErrorHandling.Move n’est pas autorisé.

  • Message Queuing dans Windows Vista prend en charge l’accusé de réception négatif, alors que Windows Server 2003 et Windows XP ne le font pas. Un accusé de réception négatif provenant du gestionnaire de files d'attente de destination provoque le placement du message rejeté dans la file d'attente de lettres mortes par le gestionnaire de files d'attente source. Par conséquent, ReceiveErrorHandling.Reject n’est pas autorisé avec Windows Server 2003 et Windows XP.

  • Message Queuing dans Windows Vista prend en charge une propriété de message qui compte le nombre de tentatives de remise d’un message. Cette propriété de nombre d’abandons n’est pas disponible sur Windows Server 2003 et Windows XP. WCF conserve le nombre d’abandons en mémoire, si bien qu’il est possible que cette propriété ne contienne pas une valeur exacte lorsque le même message est lu par plusieurs services WCF au sein d’une batterie de serveurs.

Voir aussi