Tuesday, June 24, 2014

Simple interprocess messaging using queues (2/3)

In the second entry of our messaging tutorial we will create a producer and a consumer using the RabbitMQ server in the middle.

RabbitMQ is a reliable messaging subsystem that supports the Advanced Message Queuing Protocol (AMQP). Because of this, RabbitMQ is a great choice for interoperable scenarios – the AMQP protocol is supported on multiple platforms and if for some reason there is still no implementations for a platform of your choice, you are free to create one (as AMQP just sits on top of the TCP).

RabbitMQ is a separate download but the installation is a breeze. The server also contains a web-based administration console that listens on the 15672 port (http://localhost:15672) (the console has to be enabled first).

RabbitMQ introduces an important concept that MSMQ lacks.

There are not only queues subscribers listen on but also exchanges publishers could alternatively use to publish messages. By separating publishing from subscribing and defining bindings between exchanges and queues, complex scenarios can be created where a message published on a single exchange is automatically delivered to multiple queues.

It still raises some difficulties, however. It is because there are not only multiple publishers, multiple subscribers but also – multiple message types. You still need an idea how to model the scenario. Should you create a queue for each subscriber? Or a queue per message type? Or a queue per subscriber and message type? What about exchanges then? We will try to answer these questions in the next part of the tutorial.

This time the code involves the RabbitMQ.Client library that can be accessed via NuGet. A simple producer would be:

class Program
{
    static void Main( string[] args )
    {
        var factory = new ConnectionFactory() 
        { 
            HostName = "localhost"                
        };
        using ( var connection = factory.CreateConnection() )
        {
            using ( var channel = connection.CreateModel() )
            {
                channel.QueueDeclare( "hello", false, false, false, null );
 
                string message = "Hello World!";
                var body = Encoding.UTF8.GetBytes( message );
 
                while ( true )
                {
                    Console.WriteLine( "Press key to send message" );
                    Console.ReadLine();
 
                    channel.BasicPublish( "", "hello", null, body );
 
                    Console.WriteLine( " [x] Sent {0}", message );
                }
            }
        }
    }
}

Note that the BasicPublish method publishes a byte array so that the structure of the message can be arbitrary but has to be interpreted in the same way at the consumer side. Note also that this basic example doesn’t involve any exchanges, rather, the publisher publishes the message directly onto a queue.

A simple consumer:

class Program
{
    static void Main( string[] args )
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using (var connection = factory.CreateConnection())
        {
            using (var channel = connection.CreateModel())
            {
                channel.QueueDeclare("hello", false, false, false, null);
 
                var consumer = new QueueingBasicConsumer(channel);
                channel.BasicConsume("hello", true, consumer);
 
                Console.WriteLine(" [*] Waiting for messages." +
                                         "To exit press CTRL+C");
                while (true)
                {
                    var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
 
                    var body    = ea.Body;
                    var message = Encoding.UTF8.GetString(body);
                    Console.WriteLine(" [x] Received {0}", message);
                }
            }
        }
    }
}

Pros:

  • easy to install and maintain
  • multicast is supported out of the box because of the separation of queues from exchanges
  • can be clustered and/or mirrored

Cons:

  • multiple ways to model complex scenarios, you have to decide on your own

No comments: