RabbitMQ Series Part 7: Getting Ready for Some Code Examples

Now that we have covered a lot of the introductory material for RabbitMQ, this part of the series will look at developing software to interact with the message broker as both a producer and a consumer. First we will take a look at the RabbitMQ client library. Then we will introduce the business scenario used for the sample applications. Before we start looking at the individual examples we will take a quick look at the common code shared between them. Then we will move onto the actual code examples themselves.

The code for this series can be found here.

These example will include:

  • Basic queues
  • Worker queues
  • Publisher and subscribers
  • Direct routing of queues
  • Topic based publisher and subscribers
  • Remote procedure calls

RabbitMQ client library

To develop software against RabbitMQ you will need to install the RabbitMQ client library for .NET.  Before we look at how to install the client library, let’s take a brief look at what it is. This series will not serve as an in-depth guide to the whole client library API. You can read a more in-depth document for the client library that explains the full library from the RabbitMQ site. This section will serve as an introduction to the library and the examples in the rest of this series will help you cement your understanding further.

What is contained in the Client Library?

The RabbitMQ .NET client is an implementation of an AMQP client library for C# and other .NET languages. The client library implements the AMQP specification 0-8 and 0-9. The API is closely modeled on the AMQP protocol specification with little additional abstraction, so if you have a good understanding of the AMQP protocol, then you will find the client library easy to follow.

The core API interfaces and classes are defined in the RabbitMQ.Client namespace. The main API interfaces and classes are:

  • IModel : This represents an AMQP data channel and provides most of the AMQP operations.
  • IConnection : represents an AMQP connection.
  • ConnectionFactory: constructs IConnection instances.

Some other useful interfaces and classes include:

  • ConnectionParamters: configures a ConnectionFactory.
  • QueueingBasicConsumer: receives messages delivered from the server.

Connection to a message broker

You can use the following code to connect to a RabbitMQ broker.

ConnectionFactory factory = new ConnectionFactory { HostName = "localhost", UserName = "guest", Password = "guest" };
 
IConnection _connection = _factory.CreateConnection();

The parameters to connect to the broker are:

  • HostName : The RabbitMQ server host to connect too.
  • UserName : The username to connect with. This example uses the default guest account, but in reality you should create your own account.
  • Password : The password to connect with. This example uses the default guest account, but in reality you should create your own account.

Once a connection has been made to RabbitMQ, you can then open a channel to RabbitMQ.

IModel _model = _connection.CreateModel();

The channel can now be used to send and receive messages.

Exchanges and queues

Client applications work with exchanges and queues. These are high level constructs in AMQP. Exchanges and queues must be declared before they can be used. Declaring either type of object ensures the one of that name exists. If it doesn’t exist, it is created.

The following code snippet declares and exchange and a queue, then binds them together.

channel.ExchangeDeclare(“MyExchange”, "direct");
channel.QueueDeclare(“MyQueue”);

channel.QueueBind(“MyQueue”, ExchangeName, "");

Once the exchange and queues are setup and bound together, you are free to start sending and receiving messages. We will cover how to do this when we start with the first worked example.

Installing the .NET client library

There are a few ways to get the client library setup in your application. The first way is to goto the RabbtMQ client library downloads  page and download the library directly. Once you have downloaded the library you can include a reference to “RabbitMQ.Client.dll” in your application as shown in the following screenshot.

Installing the RabbitMQ Client Library

Installing the RabbitMQ Client Library

A better way would be to use the NuGet package manager to get the library. You can do this via the NuGet packages dialog box or via the Package Manager Console. To do this via the user interface, open up the Packages dialog box in visual studio from the Tools Menu -> NuGet Package Manager -> Manage NuGet Packages for Solution.

Installing the RabbitMQ Client Library

Installing the RabbitMQ Client Library

Select the nuget.org option in the ‘Online’ tree to the left of the screen. Then in the search box on the top right hand corner of the screen, type “rabbitMQ”. This will show the RabbitMQ.Client library in the list of libraries in the middle column of the screen. Double click on this library to install it into your project.

You can also install the client library using the package manager console. To open this up, goto the tools menu and select Tools -> NuGet Package Manager -> Package Manager Console. When the console window appears type:

Install-Package RabbitMQ.Client

This will install the client library into your project. You should see output similar to the following screenshot.

Installing the RabbitMQ Client Library

Installing the RabbitMQ Client Library

You are now ready to start developing code against RabbitMQ.

Example code scenario

The code samples in the rest of this series all use a common theme of a payment provider. The theme has been kept consistent to show how the different queuing scenarios relate to each other. The samples are based around a system (the producer or publisher) making either a card payment or raising a purchase order. These card payments or purchase orders will be placed into a RabbitMQ queue setup for different scenarios.

Then there will be a consuming application or applications that will process these payments of purchase orders. Payment processors are a common use case for message queuing systems as the durable nature of the message queues means that you won’t lose payment messages. It goes without saying that payment processors are not the only use case for message queues, and you may have very different requirements, but payment processing made a good example for the purposes of this series.

Common code throughout the examples

Each of the example projects in the rest of this series uses a Common code project that contains code shared between all of the examples. Before we dive into the example project let’s take a look at the shared code.

Installing the RabbitMQ Client Library

Installing the RabbitMQ Client Library

Payment.cs

All of the example projects have the concept of placing card payment onto a queue. The examples use the Payment.cs class.

using System;
 
namespace RabbitMQ.Examples
{
    [Serializable]
    public class Payment
    {
        public decimal AmountToPay;
        public string CardNumber;        
        public string Name;
    }
}

The class is marked serializable as it will need to be serialized into a byte array before it is placed onto a queue. The class contains 3 public properties. The first is AmountToPay which is a decimal that represents a payment amount. The 2nd property, CardNumber,  is a string that represents a debit / credit card number. For the examples in this series it is just treated as an arbitrary string. The final property, Name, is the name of the person making the payment.

PurchaseOrder.cs

Some of the example projects post different types of payments onto a queue. These are card payments as shown previously and purchase orders. Again, this class is marked as serializable as it will be turned into a byte array before posting onto the queue.

using System;
 
namespace RabbitMQ.Examples
{
    [Serializable]
    public class PurchaseOrder
    {
        public decimal AmountToPay;
        public string PoNumber;        
        public string CompanyName;
        public int PaymentDayTerms;
    }
}

The first property is a decimal representing an AmountToPay. The 2nd property, PoNumber, represents a purchase order number. This is typically used when companies raise purchase orders instead of paying with cards. A pre-arranged purchase order would be set up with a vendor and the purchase order number would be logged along with the payment. The difference between this and a card payment is that the money may not show up for a number of months after the vendor has raised an invoice.

The 3rd property is the CompanyName, and this is the name of the company that the purchase order has been raised against. The 4th and final property, PaymentDayTerms, is an integer representing the number of days after an invoice is raised that the purchase order will be paid. This may typically be 30,45 or 75 days.

ObjectSerialize

The final class in the Common code project is an object serializer. This static class exposes 2 extension methods. One of the extension methods is on the Object base class to serialise that object into a byte array. We do this with a Binary Formatter for the purposes of the examples in this series. A binary formatter is a good serialization method to use if the systems you are integrating with are also implemented in .NET. If you are integrating with a Java system for example, you may want to use an XmlFormatter, but that is out the scope of this series. The key principle here is you need to turn an object into a byte array to place it onto a message queue.

The DeSerialize method is an extension method for a byte array which lets you turn a serialized byte array back into its original form, i.e. a Payment message.

using System;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace RabbitMQ.Examples
{
    public static class ObjectSerialize
    {
        public static byte[] Serialize(this Object obj)
        {
            if (obj == null)
            {
                return null;
            }
 
            using (var memoryStream = new MemoryStream())
            {
                var binaryFormatter = new BinaryFormatter();
 
                binaryFormatter.Serialize(memoryStream, obj);
 
                var compressed = Compress(memoryStream.ToArray());
                return compressed;
            }
        }
 
        public static Object DeSerialize(this byte[] arrBytes)
        {
            using (var memoryStream = new MemoryStream())
            {
                var binaryFormatter = new BinaryFormatter();               
                var decompressed = Decompress(arrBytes);
 
                memoryStream.Write(decompressed, 0, decompressed.Length);
                memoryStream.Seek(0, SeekOrigin.Begin);
 
                return binaryFormatter.Deserialize(memoryStream);                
            }            
        }
 
        private static byte[] Compress(byte[] input)
        {
            byte[] compressesData;
 
            using (var outputStream = new MemoryStream())
            {
                using (var zip = new GZipStream(outputStream, CompressionMode.Compress))
                {
                    zip.Write(input, 0, input.Length);
                }
 
                compressesData = outputStream.ToArray();
            }
 
            return compressesData;
        }
 
        private static byte[] Decompress(byte[] input)
        {
            byte[] decompressedData;
 
            using (var outputStream = new MemoryStream())
            {
                using (var inputStream = new MemoryStream(input))
                {
                    using (var zip = new GZipStream(inputStream, CompressionMode.Decompress))
                    {
                        zip.CopyTo(outputStream);
                    }
                }
 
                decompressedData = outputStream.ToArray();
            }
 
            return decompressedData;
        }
    }
}

The ObjectSerialize class also contains 2 private static methods that compress and decompress the byte array using the GZipStream class in .NET. This helps to ensure the serialized message is as small as it can be.

Payment payment1 = new Payment { AmountToPay = 25.0m, CardNumber = "1234123412341234" };
byte [] serialized = payment1.Serialize();
Payment payment_deserialized =  serialized.DeSerialize();

The above code example shows a payment message that has been constructed and is then serialized into a byte array. The serialized byte array is then immediately deserialized back into a Payment message.

In the next article we will look at the first working code example which is a basic queue.

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