Implementing Queued Messaging






Implementing Queued Messaging

Queued Messaging in Windows Communication Foundation enables clients and services to send and receive messages without requiring both applications to be running at the same time. The programming model for using queues basically consists of choosing the appropriate predefined binding or configuring your own custom binding.

Before we dive into Queued Messaging, it's important to review MSMQ and queuing in general.

Queued Messaging

If you were to call John Smith's office and ask a question, you would be engaging in synchronous messaging with John. You would be making a request and John would be providing a response in real-time.

If you were to call John Smith's office before he arrived, you would leave a voice-mail message with the question. When John Smith arrived at his office, he would see a blinking light to indicate that he had new voice-mail messages. From there, he would dial his voice-mail system and retrieve that message. He would then send a response with the answer by either phone or email.

Your message is being placed in a durable store, John Smith is notified that there are messages available, and he connects to the system to retrieve them.

Queuing is analogous to the voice-mail scenario. Conceptually, a sender initiates a communication by sending a message. Instead of going to voice mail, this message goes to a queue, where it is stored. A receiver application monitors that queue and receives messages at some point after they were delivered.

An MSMQ Primer

Microsoft Message Queue (MSMQ) is the technology Microsoft has provided to support queued messaging on the windows platform. Although MSMQ is not a new feature being introduced with WCF, it may be new to some readers. As such, this next section provides a primer.

We've given an overview of what queued messaging is; now we will begin to look at how queued messaging is done with MSMQ. To begin with, know that there are actually multiple types of queues within MSMQ. They can be placed into one of two bucketsuser-created queues and system queues.

User-Created Queues

There are several types of user-created queues. Public queues are queues that are visible on the network and listed in Active Directory. Private queues are local to the computer on which they are created. Because their information is not published, an application must know the full path or label of the private queue to access it.

In addition to private and public queues, there are two other types of user-created queues: Administration Queues and Response Queues. Administration queues contain the acknowledgment messages for messages sent within a network. Response queues contain response messages. Response messages are returned to the sender by the receiver, after a message is received.

System Queues

Systems queues have several types as well: journal, dead-letter, report, and private system queues.

Journal queues can be set up to store copies of messages sent and retrieved from a queue. Separate journals exist on both the client and the server. On the client, there is a single journal queue that logs messages sent to/from that computer. On the server side, a separate journal queue is created for each individual queue, and it tracks only those messages attached to that queue.

When messages are undeliverable or have expired, they are placed in another type of system queue, a dead-letter queue. Dead letters are stored on the computer on which the message expired. When a transactional message dies, it is placed in a variant of dead-letter queue called a transaction dead-letter queue.

Report queues contain messages that identify the route a message took to its destination. In addition, it can also contain test messages. There is one report queue per computer.

The last type is private system queues. These queues store administrative and notification messages needed by the system to process messaging actions.

Dead-Letter Queues and WCF

The dead-letter queue was discussed earlier in the chapter, and it is essentially the queue where expired or otherwise undeliverable messages are sent.

Messages can end up in the dead-letter queue for a number of reasons. This can happen if a queue quota is exceeded, if there is an authorization failure, or if a message expires.

Message delivery can be time sensitive, and the content within messages may have a set lifetime. In these cases, after the expected lifetime, the message should no longer be considered valid. The lifetime of the message can be specified in the binding, specifically the TimeToLive value.

After the TimeToLive has passed, the message is considered expired and sent to the dead-letter queue.

On Windows Vista, WCF v1.0 moves away from a traditional shared dead-letter queue across all applications, providing instead a dead-letter queue for each sending application. By moving away from a traditional shared dead-letter queue, these application-specific dead-letter queues provide a level of isolation between applications and the processing of dead letters targeted for those applications.

Poison Messages

When a message exceeds the maximum number of delivery attempts to the receiving application, it is called a poison message.

This situation can arise, for example, when applications that read messages from a queue cannot process the message immediately because of errors. Aborting the transaction in which the queued message was received leaves the message in the queue so that the message is retried under a new transaction. If the problem that is causing the abort is not corrected, the receiving application can get stuck in an infinite loop, receiving and aborting the same message until the maximum number of delivery attempts has been exceeded and a poison message results.

Handling Poison Messages in WCF

Poison message handling in WCF provides a way for the receiving application to handle poison messages. Poison message handling is configured via the bindings for an endpoint.

The binding exposes four properties that can be set: MaxRetries, MaxRetryCycles, RetryCycleDelay, and RejectAfterLastRetry.

MaxRetries determines the number of times redelivery of a message from the main queue to the application should be attempted. This is typically sufficient if there is a temporary situation that will resolve itself between the initial and final attempts. A common example cited here is a point-in-time deadlock on SQL Server.

MaxRetryCycles identifies the maximum number of retry cycles. A retry cycle consists of putting a message back into the application queue from the retry queue to attempt delivery again.

RetryCycleDelay determines the delay between retry cycles. The combination of the retry cycle and the retry cycle delay provides a way to handle issues in which a longer delay between retries resolves the issue. The delay, which defaults to 10 minutes, provides the capability to compensate for scenarios in which periodic outages interrupting message delivery can be handled smoothly.

RejectAfterLastRetry specifies how to react when the last attempt fails to deliver the message. When true, a negative acknowledgment will be sent to the sender. When false, the message will be sent to the poison queue.

If the message is sent to the poison queue, it can be processed by a separate WCF Queued Message service.

Note

WCF support for handling poison messages is supported only on Windows Vista.


WCF's Bindings for MSMQ

WCF has two bindings for use with MSMQ as a transport: NetProfileMsmqBinding and MsmqIntegrationBinding. The criteria for selecting the appropriate binding concern the type of application used by the client and service, in particular whether they are WCF or MSMQ applications.

In a scenario in which both the client and the server will be using WCF to communicate, the NetProfileMsmqBinding should be used. If either the sender or the receiver is not using WCF for communications, the MsmqIntegrationBinding should be used. This binding maps messages to/from WCF to MSMQ messages based on the direction of the integration (sending or receiving).

Using MSMQ in an integration scenario is covered in Chapter 6, "Legacy Integration."

Creating a WCF Application Using Queued Messaging

Imagine that the bank wanted to provide a value-added service to its customers in the form of bill payment. In this particular scenario, the electric company is one of the payees that the bank would like to support.

This next example is the code used by the bank to implement bill payments using queues:

  1. Open Visual Studio and create a new project. Create a new Windows Console application in C:\WCFHandsOn\Chapter5\Before\PartII.

    Name the project Service and name the solution Queues.

  2. Add a reference to System.ServiceModel.

  3. Add a reference to System.messaging.

  4. Add a reference to System.Configuration.

  5. Add a reference to System.Transactions.

  6. Rename Program.cs to service.cs.

  7. Open service.cs and add using statements such that the top of the file resembles the following:

    using System;
    using System.Configuration;
    using System.Messaging;
    using System.ServiceModel;
  8. Define the service contract for IBillPayment. We have one operation, PayBill, and it is defined as a one-way operation:

    {
          // Define a service contract.
          [ServiceContract]
          public interface IBillPayment
          {
              [OperationContract(IsOneWay=true)]
              void PayBill(int AccountNbr, int ElectricAccountNbr, double Amount);
    
    }
  9. Define the implementation of the class, and insert code for the PayBill operation. Here we will write out the information that is received to the console.

    Note

    Note that this service is using queues, but the interface is the same as we've seen for other transports. WCF interacts with the queues behind the scenes, and allows us to focus on writing implementation code for our business logic.

    In the future, if we choose to use the wsHttpBinding rather than queuing, it can be done in the configuration file with no changes to the code. This underscores the power of the declarative model within WCF that provides you the ability to change the underlying protocols and bindings for transport totally outside the actual application logic.


    // Service class which implements the service contract.
    // Added code to write output to the console window
    public class BillPaymentService : IBillPayment
    {
        [OperationBehavior]
        public void PayBill(int AccountNbr, int ElectricAccountNbr, double Amount)
        {
    
            Console.WriteLine("Received Request to Pay Electric Company Account {0} the amount
     of {1} from local account {2}", ElectricAccountNbr, Amount.ToString(), AccountNbr);
        }
  10. In the service.cs file, add the code to host the service.

    Note

    Note that we use System.Messaging solely for the purpose of creating queues; we do not use System.Messaging for anything related to the actual service:


    // 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 (!MessageQueue.Exists(queueName))
          MessageQueue.Create(queueName, true);
    
        // Get the base addresses.
        // Including an Http base address for
        // WS-MetaDataExchange requests is
        // useful to generate a proxy for the client
        string httpBaseAddress =
          ConfigurationManager.AppSettings["httpBaseAddress"];
        string queueBaseAddress =
          ConfigurationManager.AppSettings["queueBaseAddress"];
    
        // Create a ServiceHost for the BillPayment type.
        using (ServiceHost serviceHost =
            new ServiceHost(
              typeof(BillPaymentService),
              new Uri[]{
                new Uri(httpBaseAddress),
                new Uri(queueBaseAddress)}))
        {
          serviceHost.Open();
          Console.WriteLine("The Bill Payment Service is online.");
          Console.WriteLine("Press <ENTER> to terminate service.");
          Console.WriteLine();
          Console.ReadLine();
    
        // Close the ServiceHostBase to shut down the service.
        serviceHost.Close();
      }
    }
  11. Create a new application configuration file and populate the file with the following configuration settings.

    Note that we provide an http endpoint for the base address. This is used to expose metadata for our service using http, whereas the service itself uses MSMQ and the netMsmqBinding:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <appSettings>
          <add key="queueName" value=".\private$\BillPay" />
          <add key ="httpBaseAddress"
            value="http://localhost:8000/BillPay"/>
          <add key ="queueBaseAddress"
            value="net.msmq://localhost/private/"/>
    </appSettings>
    <system.serviceModel>
        <services>
          <service
            type="WCFHandsOn.BillPaymentService">
            <endpoint address="BillPay"
              binding="netMsmqBinding"
              bindingConfiguration="DefaultMsmqBinding"
              contract="WCFHandsOn.IBillPayment" />
          </service>
        </services>
        <bindings>
          <netMsmqBinding>
            <binding name="DefaultMsmqBinding" />
          </netMsmqBinding>
        </bindings>
      </system.serviceModel>
    </configuration>

Creating the Client for the Service

It is now time to create the client to interact with our service.

  1. Add a new project to the solution.

  2. Create a new Windows Console application in C:\WCFHandsOn\Chapter5\Before\PartII.

  3. Name the project Client.

  4. Add a reference to System.ServiceModel.

  5. Add a reference to System.Messaging.

  6. Add a reference to System.Configuration.

  7. Add a reference to System.Transactions.

  8. Rename Program.cs to client.cs.

  9. Start the Service application.

    Because the metadata for the service is exposed using http, you can query it using SvcUtil.exe.

    Run SvcUtil.exe against the endpoint specified in the services App.Config file. This will query the metadata for the service and generate a proxy class for the service.

  10. From the command prompt, execute the following:

    "C:\Program Files\Microsoft SDKs\Windows\v1.0\Bin\SvcUtil.exe" http://localhost:8000/BillPay

    This will generate two files, BillPaymentService.cs and Output.config.

  11. Add both files to the Client project.

  12. Rename Output.config to App.Config.

    With the proxy for the service created, you can now write the code to call the queued service.

  13. In the main method of client.cs, add the following lines of code:

    static void Main()
    {
        // Create a proxy
        using (BillPaymentProxy proxy = new BillPaymentProxy())
        {
           //Create a transaction scope.
           using (TransactionScope scope =
              new TransactionScope(TransactionScopeOption.Required))
           {
    
             proxy.PayBill(12345,67890,50);
             Console.WriteLine("Paying $50 towards acount 67890");
    
             proxy.PayBill(22345, 77890, 100);
             Console.WriteLine("Paying $100 towards account 77890");
    
             // Complete the transaction.
             scope.Complete();
        }
      }
      Console.WriteLine();
      Console.WriteLine("Press <ENTER> to terminate client.");
      Console.ReadLine();
    }

You are now ready to test your queued messaging solution.

Test #1Client and Service Online

The first test will show how the solution will respond in an ideal scenario where both client and service are online at the same time.

  1. Start the Service.

  2. Start the Client.

The client will call the service, which will utilize queues behind the scenes. The service will pick up the messages from the queue and write out that the bill payments have occurred.

The expected outcome is shown in Figure.

4. Screenshot of the resulting test.


Test #2Service Offline When Message Sent, Online Later

In this test, we will simulate a disconnected scenario.

  1. Start the client.

  2. The client will call the service, which again utilizes queues behind the scenes.

  3. Stop the client.

  4. Start the service.

Notice that the service comes online, detects the messages, and processes the messages the client sent earlier.

This is because the messages were placed on the queue by the client, and the service retrieved the messages from the queue when it went online.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows