Experimenting with RabbitMQ – LoggingApplication example


Preamble

This is part 2 of a series of blogposts about RabbitMQ. This series aims to provide more information (I cannot vouch for the accuracy of the information as I’m a beginner at RabbitMQ) concerning a series of posts by Derek Greer.

Part 1: Experimenting with RabbitMQ – HelloWorldExample

Part 2: Experimenting with RabbitMQ – LoggingApplication example

Part 3: Experimenting with RabbitMQ – Fanout exchange

Part 4: Experimenting with RabbitMQ – Topic exchange

————————

These are some notes based upon this blogpost.

In this example, the Producer and Consumer applications differ from the HelloWorldExample in the following ways.

Logging Application’s Producer

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using RabbitMQ.Client;

namespace Producer
{
  class Program
  {
    static void Main(string[] args)
    {
      Thread.Sleep(1000);
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
      IModel channel = connection.CreateModel();

      channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
      string value = DoSomethingInteresting();
      string logMessage = string.Format("{0}: {1}", TraceEventType.Information, value);

      byte[] message = Encoding.UTF8.GetBytes(logMessage);
      channel.BasicPublish("direct-exchange-example", string.Empty, null, message);

      channel.Close();
      connection.Close();
    }

    static string DoSomethingInteresting()
    {
      return Guid.NewGuid().ToString();
    }
  }
}

In this particular instance, the producer explicitly declares a “Direct Exchange” but there is no queue declared. Instead, when we publish the message, we send it to the “direct-exchange-example” exchange and pass the empty string as the routing key.

The author also emphasises the following:

Note that our logging example’s Producer differs from our Hello World’s Producer in that we didn’t declare a queue this time.  
In our Hello World example, we needed to run our Producer before the Consumer since the Consumer simply retrieved a single message and exited.  
Had we published to the default exchange without declaring the queue first, our message would simply have been discarded by the server before 
the Consumer had an opportunity to declare and bind the queue.

So I decided to go back and experiment and declared the empty string as being the queue and the routing key.

HelloWorld’s Producer

static void Main()
{
    const string queue = "";//"hello-world-queue";
    var connectionFactory = new ConnectionFactory();
    IConnection connection = connectionFactory.CreateConnection();

    IModel channel = connection.CreateModel();

    channel.QueueDeclare(queue, false, false, false, null);

    byte[] message = Encoding.UTF8.GetBytes("Hello, World!");
    channel.BasicPublish(string.Empty, queue, null, message);

    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
    channel.Close();
    connection.Close();
}

When I ran HelloWorld’s Producer, three things of note happened:

  1. A message was indeed sent to the server
  2. As Derek Greer pointed out, the server discarded the message i.e. it did not get queued.
  3. Actually a queue was created. It was named amq.gen-wk-vqJ4WsDriToH7XOs0sg  with the part in red being randomly generated. Hence running, this code again created another queue.

EmptyStringForQueue

Logging Application’s Consumer

using System;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace Consumer
{
  class Program
  {
    static void Main(string[] args)
    {
      var connectionFactory = new ConnectionFactory();
      IConnection connection = connectionFactory.CreateConnection();
      IModel channel = connection.CreateModel();

      channel.ExchangeDeclare("direct-exchange-example", ExchangeType.Direct);
      channel.QueueDeclare("logs", false, false, true, null);
      channel.QueueBind("logs", "direct-exchange-example", "");

      var consumer = new QueueingBasicConsumer(channel);
      channel.BasicConsume("logs", true, consumer);

      var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

      string message = Encoding.UTF8.GetString(eventArgs.Body);
      Console.WriteLine(message);

      channel.Close();
      connection.Close();
      Console.ReadLine();
    }
  }
}

In the consumer code, there are various aspects which are interesting. First, we are creating a queue which we are calling “logs” but the point to take from this is that the “auto-delete” has been set to true (We’ll return to this later).

The second point to note is that we are binding the queue to the exchange:

channel.QueueBind("logs", "direct-exchange-example", "");

We are retrieving the message differently to HelloWorld’s Consumer:

var consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("logs", true, consumer);
var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

The fact that we are using a BasicConsume instead of the BasicGet will cause the queue to be deleted automatically (remember auto-delete mentioned above) as soon as the application is terminated.

Also, just to complete what Derek Greer mentioned in his post, the Dequeue method is a blocking call. What that means is if we were to run the Consumer application first, execution would be blocked on

var eventArgs = (BasicDeliverEventArgs) consumer.Queue.Dequeue();

until we ran the Producer application which published a message.

One thing that still puzzles me though is this. In the Producer code, when we published the message we didn’t assign a particular routing key.

channel.BasicPublish("direct-exchange-example", string.Empty, null, message);

Then when we retrieved the message, it somehow got put on the “logs” queue.

EDIT: Answer to the last puzzle.

This was in fact addressed in the original post and I quote:

To associate our logs queue with our exchange, we use the QueueBind() method providing the name of the queue, the name of the exchange, and the binding key to filter messages on:
channel.QueueBind("logs", "direct-exchange-example", "");
Advertisements
Posted in .NET, ALT.NET, RabbitMQ

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: