Getting started with RabbitMQ in Windows using C#

In a previous blog, I wrote how excited I was by ZeroMQ, a very lightweight brokerless messaging system. I wanted to also try RabbitMQ using C# on my Windows development machine, but couldn't find any step-by-step introduction to get me to the point of running my first program. I'm there now, so I thought it would be good to remedy that.

I'd like to note that I'm not a RabbitMQ or messaging whiz. What you see here may or may not be best practice. If it's not, hopefully someone will chime in with some comments to put me back on the right path.

Here are the steps I took to install RabbitMQ and create a couple of simple C# clients to check that everything was working.

  1. Download the Windows bundle zip file from the RabbitMQ download page. The current version at the time of writing is 1.8.0. It contains the Erlang installer, the RabbitMQ server, and bindings for Java and C#.
  2. Unzip the bundle.
  3. Install Erlang using the OTP installer by running otp_win32_R13B03.exe from the unzipped bundle.
  4. Create a subdirectory "RabbitMQ" in "C:\Program Files". (Or "C:\Program Files (x86)" if you are running on 64-bit).
  5. Unzip the rabbitmq-server-windows-1.8.0 zipfile in the bundle to a directory rabbitmq-server-windows-1.8.0. 
  6. Copy the rabbitmq-server-windows-1.8.0 directory you just unzipped to the RabbitMQ directory you just created. Copy the rabbitmq-server-windows-1.8.0 directory itself rather than its contents. ie You should have a directory "C:\Program Files\RabbitMQ\rabbitmq_server-1.8.0" when you have done this.
  7. You need to add ERLANG_HOME to the environment variables for your system so RabbitMQ can find Erlang. I can't remember precisely how to do this in XP - I think it's in Control Panel...System... somewhere, and I don't have Windows 7 yet. Perhaps someone could comment for these versions of Windows. In Vista, this is done by:
    1. Open the control panel.
    2. Search "environment". As you type, the option to "Edit the system environment variables" should present itself. Click this - you will be asked for permission to take this action.
    3. In the System Properties...Advanced tab which appears, click the "Environment Variables..." button.
    4. Click the bottom "New..." button to add a system variable.
    5. Enter "ERLANG_HOME" as the variable name.
    6. Enter "C:\Program Files\erl5.7.4" as the variable value for 32-bit systems. Use "C:\Program Files (x86)\erl5.7.4" for 64-bit systems.
    7. Click Ok, Ok, Ok and close the control panel.
  8. Run the server by running "C:\Program Files\RabbitMQ\rabbitmq_server-1.8.0\sbin\rabbitmq-server.bat". A command-line window should appear. When it says "broker running" the server is running ok.
  9. Install the .Net libraries by running the rabbitmq-dotnet-client-1.8.0.msi installer in the unzipped bundle.

You can also install RabbitMQ as a service. The RabbitMQ documentation has more details on this.

Ok, so now you've installed RabbitMQ, and you have a RabbitMQ broker running (that's the server that all your message producers and message consumers will connect to). Before going any further, it's worth taking a little time to come up to speed with the concepts behind RabbitMQ. Dmitriy Samovskiy has an excellent, and quick, set of introduction slides which should tell you what you need to know (when I clicked publish, I saw that Posterous automatically embedded this - kudos, Posterous crew):

In case you are too impatient to read through that, the gist is that a publisher application sends messages to an exchange, which sits on the server. Message queues sit on the server and filter messages from exchanges. Finally a consumer application consumes messages from a message queue.

A simple publisher

Let's start by writing an application that publishes a message each second which contains "Hello World":

  1. Start a new project in Visual C#, called "RabbitMQPub".
  2. Add a reference to "RabbitMQ Client Library for .Net", by right-clicking the "References" entry in the Solution Explorer, clicking "Add Reference...", selecting the ".NET" tab, and double-clicking the "RabbitMQ Client Library for .Net" entry.
  3. In the program you will want to add using statements to bring in the appropriate RabbitMQ namespaces:
    using RabbitMQ.Client;using RabbitMQ.Client.Framing.v0_8;
    </span>
     
  4. The first step is to create a ConnectionFactory, and configure it:
    ConnectionFactory factory = new ConnectionFactory();
    factory.Protocol = Protocols.FromEnvironment();
    factory.HostName = "localhost";         
    

    There are other configuration items, which are taking default values here. This will work on a virgin RabbitMQ instance, but in production you should look at setting up access control and VHosts.
  5. Then from the ConnectionFactory, create a Connection object:
    IConnection conn = factory.CreateConnection();
    
  6. And from the connection, create a Channel object:
    IModel ch = conn.CreateModel();
    
  7. We need to create an exchange to publish our messages to:
    ch.ExchangeDeclare("exch", ExchangeType.Direct);
    
     
  8. And a queue to filter messages from the exchange. A queue is made to filter messages from a particular exchange by "binding" it to that exchange:
     
    ch.QueueDeclare("queue");
    ch.QueueBind("queue", "exch", "key", false, null);
    
     
  9. Then we loop, using the Channel object's BasicPublish method to publish a new message every second, and printing a message to the display to indicate we have done so.
     
                while (true)
                {
                    Console.WriteLine("Sending message.");
                    byte[] messageBody = Encoding.UTF8.GetBytes("Hello world");
                    ch.BasicPublish("exch", "key", null, messageBody);
                    Thread.Sleep(1000);
                }
    
     
    Note that we are expected to present byte arrays to send in a message. When sending strings, it's a good idea to use UTF-8 encoding as shown. 
  10. Finally, we close the channel and the connection. This will never execute in this case, and the channel and connection will be shutdown by program termination instead, but if you are writing a client that isn't guaranteed to loop forever, this is how you should terminate the connection:
     
    ch.Close(Constants.ReplySuccess, "Closing the channel");
    conn.Close(Constants.ReplySuccess, "Closing the connection");
    
     

A simple consumer

We now need to create a simple application to listen for these messages, and print them.

  1. Create a new project Follow steps 2-6 and 10 above. This should connect to the RabbitMQ broker, but otherwise is inactive.
  2. Add code to consume messages from a queue:
    QueueingBasicConsumer consumer = new QueueingBasicConsumer(ch); ch.BasicConsume("queue", null, consumer);
    
  3. In an infinite loop, listen for messages, decoding each one, and printing its body
                while (true)
                {
                    BasicDeliverEventArgs e = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                    IBasicProperties props = e.BasicProperties;
                    byte[] body = e.Body;
                    Console.WriteLine("Received message: " + Encoding.UTF8.GetString(body));
                    ch.BasicAck(e.DeliveryTag, false);
                }
    

    Note that messages must be acknowledged using BasicAck, or the broker will resend them.
     

Source code

I have made the source code for these projects, as well as other sample projects, through GitHub: http://github.com/edparcell/Samples-CS

Where's the Fudge?

In a previous post, I mentioned that I would like to give an example of using Fudge to encode messages. That is still my intention. However, I thought this blog post had become long enough already, so I'd like to defer that to another post.

Comments

Some random comments on RabbitMQ so far:

  • The bundle seems quite large. I know the bulk of this is the OTP installer, but it makes it quite hard to consider embedding RabbitMQ in a lot of small-scale cases, such as deploying to a single end-user machine. Not a problem for an enterprise perhaps, but this is the case I have right now.
    [Update: I'd like to clarify that 80 megs is not a huge download by modern standards, especially for such a useful and full-featured product. Just that it would look odd to my end-users if I replace the named pipes in a few programs with embedded RabbitMQ, and the installer balloons from 1 meg to 80 megs].
  • Would be nice to create an installer to automate these steps, so installation is simply clicking "Next" a few times.
  • The .Net user guide seems a little out-of-sync with the latest library in places. Not terribly, but it throws you a curveball sometimes, such as suggesting that ConnectionFactory.CreateConnection takes 3 parameters, when in the latest version you should set some member variables on the factory and call the method with no parameters.

Overall, RabbitMQ seems like a great application, albeit a little tough to get started in on .Net. Hopefully this short tutorial will help with that.

I'd gratefully welcome any corrections - as I said, I'm no messaging guru, so if I've met any errors, let me know and I'll correct them.