Partager via


Files d’attente de lettres mortes

L’exemple DeadLetter montre comment gérer et traiter les messages ayant échoué. Il est basé sur l’exemple de Liaison MSMQ traitée. Cet exemple utilise la netMsmqBinding liaison. Le service est une application console auto-hébergée pour vous permettre d’observer le service recevant des messages mis en file d’attente.

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cette rubrique.

Remarque

Cet exemple montre chaque file d’attente de lettres mortes d’application qui est uniquement disponible sur Windows Vista. L’exemple peut être modifié pour utiliser les files d’attente à l’échelle du système par défaut pour MSMQ 3.0 sur Windows Server 2003 et Windows XP.

Dans la communication en file d'attente, le client communique avec le service à l’aide d’une file d’attente. Plus précisément, le client envoie des messages à une file d’attente. Le service reçoit des messages de la file d’attente. Le service et le client n'ont donc pas besoin d'être en cours d’exécution simultanément pour communiquer via une file d’attente.

Étant donné que la communication mise en file d’attente peut impliquer une certaine latence, vous pouvez associer une durée de validité au message pour vous assurer qu’il ne soit pas remis à l’application s'il dépasse cette durée. Il existe également des cas où une application doit être informée si un message a échoué. Dans tous ces cas, par exemple lorsque la durée de vie du message a expiré ou que le message a échoué, le message est placé dans une file d’attente de lettres mortes. L’application d’envoi peut ensuite lire les messages dans la file d’attente de lettres mortes et effectuer des actions correctives allant d'aucune action à la correction des raisons de l’échec de remise et à la réexpédition du message.

La file d’attente de lettres mortes dans la liaison NetMsmqBinding est exprimée dans les propriétés suivantes :

  • La propriété DeadLetterQueue permet d’exprimer le type de file d’attente de lettres mortes requis par le client. Cette énumération a les valeurs suivantes :

  • None : aucune file d’attente de lettres mortes n’est requise par le client.

  • System  la file d’attente de lettres mortes du système est utilisée pour stocker les lettres mortes. La file d'attente de lettres mortes du système est partagée par toutes les applications exécutées sur l'ordinateur.

  • Custom : une file d’attente de lettres mortes personnalisée spécifiée à l’aide de la propriété CustomDeadLetterQueue est utilisée pour stocker des messages morts. Cette fonctionnalité est disponible uniquement sur Windows Vista. Elle est utilisée lorsque l'application doit utiliser sa propre file d'attente de lettres mortes au lieu de la partager avec d'autres applications exécutées sur le même ordinateur.

  • La propriété CustomDeadLetterQueue permet d’exprimer la file d’attente spécifique à utiliser comme file d’attente de lettres mortes. Cette option est disponible uniquement dans Windows Vista.

Dans cet exemple, le client envoie un lot de messages au service à partir de l’étendue d’une transaction et spécifie une valeur arbitrairement faible pour « durée de vie » pour ces messages (environ 2 secondes). Le client indique également la file d'attente de lettres mortes personnalisée à utiliser pour mettre les messages qui ont expiré en file d'attente.

L'application cliente peut lire les messages dans la file d'attente des lettres mortes et peut soit réessayer d'envoyer le message, soit corriger l'erreur qui a entraîné son placement dans la file d'attente des lettres mortes avant d'envoyer le message. Dans l’exemple, le client affiche un message d’erreur.

Le contrat de service est IOrderProcessor, comme indiqué dans l’exemple de code suivant.

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

Le code de service dans l’exemple est celui de la liaison MSMQ traitée.

La communication avec le service a lieu dans le cadre d'une transaction. Le service lit les messages de la file d’attente, effectue l’opération, puis affiche les résultats de l’opération. L'application crée également une file d'attente de lettres mortes pour les messages de lettres mortes.

//The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.

//Client implementation code.
class Client
{
    static void Main()
    {
        // Get MSMQ queue name from app settings in configuration
        string deadLetterQueueName = ConfigurationManager.AppSettings["deadLetterQueueName"];

        // Create the transacted MSMQ queue for storing dead message if necessary.
        if (!MessageQueue.Exists(deadLetterQueueName))
            MessageQueue.Create(deadLetterQueueName, true);

        // Create a proxy with given client endpoint configuration
        OrderProcessorClient client = new OrderProcessorClient("OrderProcessorEndpoint");

        // Create the purchase order
        PurchaseOrder po = new PurchaseOrder();
        po.CustomerId = "somecustomer.com";
        po.PONumber = Guid.NewGuid().ToString();

        PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
        lineItem1.ProductId = "Blue Widget";
        lineItem1.Quantity = 54;
        lineItem1.UnitCost = 29.99F;

        PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
        lineItem2.ProductId = "Red Widget";
        lineItem2.Quantity = 890;
        lineItem2.UnitCost = 45.89F;

        po.orderLineItems = new PurchaseOrderLineItem[2];
        po.orderLineItems[0] = lineItem1;
        po.orderLineItems[1] = lineItem2;

        //Create a transaction scope.
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
        {
            // Make a queued call to submit the purchase order
            client.SubmitPurchaseOrder(po);
            // Complete the transaction.
            scope.Complete();
        }

        client.Close();

        Console.WriteLine();
        Console.WriteLine("Press <ENTER> to terminate client.");
        Console.ReadLine();
    }
}

La configuration du client spécifie une courte durée pour que le message atteigne le service. Si le message ne peut pas être transmis dans la durée spécifiée, le message expire et est déplacé vers la file d’attente de lettres mortes.

Remarque

Il est possible que le client remet le message à la file d’attente du service dans le délai spécifié. Pour être sûr de voir le service de lettres mortes en action, vous devez exécuter le client avant de démarrer le service. Le message expire et est remis au service de lettres mortes.

L'application doit définir la file d'attente à utiliser comme file d'attente de lettres mortes. Si aucune file d’attente n’est spécifiée, la file d’attente de lettres mortes transactionnelle du système par défaut est utilisée pour mettre en file d’attente les messages morts. Dans cet exemple, l'application cliente spécifie sa propre file d'attente de lettres mortes.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- use appSetting to configure MSMQ Dead Letter queue name -->
    <add key="deadLetterQueueName" value=".\private$\ServiceModelSamplesOrdersAppDLQ"/>
  </appSettings>

  <system.serviceModel>
    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                binding="netMsmqBinding"
                bindingConfiguration="PerAppDLQBinding"
                contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="PerAppDLQBinding"
                 deadLetterQueue="Custom"
                 customDeadLetterQueue="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                 timeToLive="00:00:02"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>

</configuration>

Le service de lettres mortes lit les messages à partir de la file d'attente de lettres mortes. Le service de message de lettres mortes implémente le contrat IOrderProcessor. Sa mise en œuvre ne consiste toutefois pas à traiter les commandes. Le service de message de lettres mortes est un service client et n'a pas la fonctionnalité de traitement de commandes.

Remarque

La file d'attente de lettres mortes est une file d'attente cliente et est locale sur le gestionnaire de files d'attente client.

L'implémentation du service de lettres mortes vérifie le motif d'échec de remise d'un message et prend les mesures adéquates. La raison d’un échec de message est capturée dans deux énumérations, DeliveryFailure et DeliveryStatus. Vous pouvez récupérer le MsmqMessageProperty à partir du OperationContext comme indiqué dans l'exemple de code suivant :

public void SubmitPurchaseOrder(PurchaseOrder po)
{
    Console.WriteLine("Submitting purchase order did not succeed ", po);
    MsmqMessageProperty mqProp =
                  OperationContext.Current.IncomingMessageProperties[
                  MsmqMessageProperty.Name] as MsmqMessageProperty;
    Console.WriteLine("Message Delivery Status: {0} ",
                                                mqProp.DeliveryStatus);
    Console.WriteLine("Message Delivery Failure: {0}",
                                               mqProp.DeliveryFailure);
    Console.WriteLine();
    …
}

Les messages dans la file d'attente de lettres mortes sont des messages adressés au service qui est le traite le message. Par conséquent, lorsque le service de lettres mortes lit des messages de la file d’attente, la couche de canal WCF (Windows Communication Foundation) détecte l’incompatibilité entre les points de terminaison et ne distribue pas le message. Dans ce cas, le message est adressé au service de traitement des commandes, mais il est reçu par le service des messages de lettres mortes. Pour recevoir un message adressé à un autre point de terminaison, un filtre d’adresse correspondant à n’importe quelle adresse est spécifiée dans le ServiceBehavior. Cela est requis pour traiter correctement les messages lus à partir de la file d'attente de lettres mortes.

Dans cet exemple, le service de message de lettres mortes renvoie le message si la raison de l’échec est que le message a expiré. Pour toutes les autres raisons, il affiche l’échec de remise, comme indiqué dans l’exemple de code suivant :

// Service class that implements the service contract.
// Added code to write output to the console window.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Single, AddressFilterMode=AddressFilterMode.Any)]
public class PurchaseOrderDLQService : IOrderProcessor
{
    OrderProcessorClient orderProcessorService;
    public PurchaseOrderDLQService()
    {
        orderProcessorService = new OrderProcessorClient("OrderProcessorEndpoint");
    }

    [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
    public void SubmitPurchaseOrder(PurchaseOrder po)
    {
        Console.WriteLine("Submitting purchase order did not succeed ", po);
        MsmqMessageProperty mqProp = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty;

        Console.WriteLine("Message Delivery Status: {0} ", mqProp.DeliveryStatus);
        Console.WriteLine("Message Delivery Failure: {0}", mqProp.DeliveryFailure);
        Console.WriteLine();

        // resend the message if timed out
        if (mqProp.DeliveryFailure == DeliveryFailure.ReachQueueTimeout ||
            mqProp.DeliveryFailure == DeliveryFailure.ReceiveTimeout)
        {
            // re-send
            Console.WriteLine("Purchase order Time To Live expired");
            Console.WriteLine("Trying to resend the message");

            // reuse the same transaction used to read the message from dlq to enqueue the message to app. queue
            orderProcessorService.SubmitPurchaseOrder(po);
            Console.WriteLine("Purchase order resent");
        }
    }

    // Host the service within this EXE console application.
    public static void Main()
    {
        // Create a ServiceHost for the PurchaseOrderDLQService type.
        using (ServiceHost serviceHost = new ServiceHost(typeof(PurchaseOrderDLQService)))
        {
            // Open the ServiceHostBase to create listeners and start listening for messages.
            serviceHost.Open();

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

L'exemple suivant affiche la configuration pour un message de lettres mortes :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.PurchaseOrderDLQService">
        <!-- Define NetMsmqEndpoint in this case, DLQ end point to read messages-->
        <endpoint address="net.msmq://localhost/private/ServiceModelSamplesOrdersAppDLQ"
                  binding="netMsmqBinding"
                  bindingConfiguration="DefaultBinding"
                  contract="Microsoft.ServiceModel.Samples.IOrderProcessor" />
      </service>
    </services>

    <client>
      <!-- Define NetMsmqEndpoint -->
      <endpoint name="OrderProcessorEndpoint"
                 address="net.msmq://localhost/private/ServiceModelSamplesDeadLetter"
                 binding="netMsmqBinding"
                 bindingConfiguration="SystemDLQBinding"
                 contract="IOrderProcessor" />
    </client>

    <bindings>
      <netMsmqBinding>
        <binding name="DefaultBinding" />
        <binding name="SystemDLQBinding"
                 deadLetterQueue="System"/>
      </netMsmqBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Lors de l'exécution de l'exemple, il y a trois exécutables à lancer pour voir comment fonctionne la file d'attente de lettres mortes pour chaque application : le client, le service et un service de gestion des lettres mortes qui lit à partir de la file d'attente de lettres mortes pour chaque application et redirige le message au service. Toutes sont des applications console avec une sortie dans les fenêtres de console.

Remarque

Étant donné que la mise en file d’attente est en cours d’utilisation, le client et le service n’ont pas besoin d’être opérationnel en même temps. Vous pouvez exécuter le client, l'arrêter, puis démarrer le service, et il continuera à recevoir ses messages. Vous devez démarrer le service et l’arrêter afin que la file d’attente puisse être créée.

Lors de l’exécution du client, le client affiche le message :

Press <ENTER> to terminate client.

Le client a essayé d'envoyer le message mais avec un délai d'attente court, le message a expiré et est maintenant mis dans la file d'attente de lettres mortes pour chaque application.

Vous exécutez alors le service de lettres mortes qui lit le message, affiche le code d'erreur et renvoie le message au service.

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

Submitting purchase order did not succeed
Message Delivery Status: InDoubt
Message Delivery Failure: ReachQueueTimeout

Purchase order Time To Live expired
Trying to resend the message
Purchase order resent

Le service démarre, puis lit le message renvoyé et le traite.

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

Processing Purchase Order: 97897eff-f926-4057-a32b-af8fb11b9bf9
        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

Pour configurer, générer et exécuter l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Si le service est exécuté en premier, il vérifie que la file d’attente est présente. Si la file d’attente n’est pas présente, le service en crée un. Vous pouvez d’abord exécuter le service pour créer la file d’attente, ou en créer un via le Gestionnaire de files d’attente MSMQ. Suivez ces étapes pour créer une file d’attente dans Windows 2008.

    1. Ouvrez le Gestionnaire de serveur dans Visual Studio 2012.

    2. Développez l’onglet Fonctionnalités.

    3. Cliquez avec le bouton droit sur file d’attente de messages privés, puis sélectionnez Nouveau, file d’attente privée.

    4. Activez la case à cocher Transactionnelle.

    5. Entrez ServiceModelSamplesTransacted comme nom de la nouvelle file d’attente.

  3. Pour générer l’édition C# ou Visual Basic .NET de la solution, conformez-vous aux instructions figurant dans Building the Windows Communication Foundation Samples.

  4. Pour exécuter l’exemple dans une configuration à un ou plusieurs ordinateurs, modifiez les noms de file d’attente de manière appropriée, en remplaçant localhost par le nom complet de l’ordinateur et suivez les instructions de l’exécution des exemples Windows Communication Foundation.

Pour exécuter l’exemple sur un ordinateur joint à un groupe de travail

  1. Si votre ordinateur ne fait pas partie d’un domaine, désactivez la sécurité du transport en définissant le mode d’authentification et le niveau None de protection comme indiqué dans l’exemple de configuration suivant :

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

    Assurez-vous que le point de terminaison est associé à la liaison en définissant l'attribut bindingConfiguration du point de terminaison.

  2. Veillez à modifier la configuration sur DeadLetterService, le serveur et le client avant d’exécuter l’exemple.

    Remarque

    Définir security mode sur None équivaut à définir la sécurité de MsmqAuthenticationMode, MsmqProtectionLevel et Message sur None.

Commentaires

Avec le transport de liaison netMsmqBinding, la sécurité est activée par défaut. Deux propriétés, MsmqAuthenticationMode et MsmqProtectionLevel, ensemble déterminent le type de sécurité du transport. Par défaut, le mode d’authentification est défini Windows sur et le niveau de protection est défini sur Sign. Pour que MSMQ fournisse la fonctionnalité d’authentification et de signature, elle doit faire partie d’un domaine. Si vous exécutez cet exemple sur un ordinateur qui ne fait pas partie d’un domaine, vous recevez l’erreur suivante : « Le certificat de mise en file d’attente de message interne de l’utilisateur n’existe pas ».