Tuesday, June 24, 2014

Simple interprocess messaging using queues (1/3)

This entry opens a 3-part tutorial on simple publish-subscribe implementations in .NET. We will try three different approaches:

  • MSMQ
  • RabbitMQ
  • MassTransit over MSMQ/RabbitMQ

and try to compare their pros and cons.

Messaging using queues is considered an important architectural pattern. It still provides the flexibility of direct interoperable interprocess communication but because of a queue in the middle between entities, heavy load peaks no longer raise immediate performance issues.

Each of our simple examples will involve a producer and a consumer. The producer will create messages and publish them onto the messaging subsystem. The consumer will await incoming messages and just print them on the console.

We start with MSMQ. Microsoft Message Queues is a well-established implementation of messaging infrastructure for Windows. Queues are persistent and there is a built-in administration snap-in available. It also supports transactions and multicasting.

The producer:

class Program
{
    static void Main( string[] args )
    {
        if ( !MessageQueue.Exists( @".\Private$\ExampleQueue" ) )
            MessageQueue.Create( @".\Private$\ExampleQueue" );
 
        MessageQueue mq = new MessageQueue( @".\Private$\ExampleQueue" );
 
        while ( true )
        {
            Console.WriteLine( "Press key to send a message" );
            Console.ReadLine();
 
            mq.Send( "Hello world form MSMQ", "Title" );
 
            Console.WriteLine( "Message sent" );
        }
 
        Console.ReadLine();
    }
}

Although in this example I am just sending a string, the Send method accepts any object. It tries to serialize it and puts it into a queue, in this example the ExampleQueue.

The consumer:

class Program
{
    static void Main( string[] args )
    {
        if ( !MessageQueue.Exists( @".\Private$\ExampleQueue" ) )
            MessageQueue.Create( @".\Private$\ExampleQueue" );
 
        MessageQueue mq = new MessageQueue( @".\Private$\ExampleQueue" );
 
        while ( true )
        {
            Console.WriteLine( "Listening to messages..." );
 
            Message msg = mq.Receive();
            msg.Formatter = new XmlMessageFormatter( new [] { typeof( string ) } );
 
            Console.WriteLine( "Message says: {0}", msg.Body );
        }
 
        Console.WriteLine( "Message sent" );
        Console.ReadLine();
    }
}

The consumer uses a blocking Receive method (note that there is also an async BeginReceive). What is interesting there is that a formatter can be attached to received message so that instead of reading the BodyStream and manually deserializing the contents, the message object does deserialization on its own and the Body property contains a reference to the deserialized message.

Such simple example raises an immediate question on how to build a more complicated scenario involving multiple publishers and multiple subscribers and some subscription rules. Unfortunately, there are no simple answers to the question.

In theory, the multicast feature should be an answer but in practice, we have found that it just doesn’t work sometimes. It’s like it works for days and then suddenly messages are not delivered and the server has to be restarted. Despite hours spent on digging into the issue, we haven’t found any single reason for the issue.

Pros:

  • MSMQ is built into the operating system, no need to install any third-party components
  • the API is a part of the Base Class Library
  • supports sending complex objects

Cons:

No comments: