Integrating Ruby and C# with RabbitMQ

Lightweight Messaging

Traditionally, enterprise integration has been accomplished using
integration server products like Microsoft BizTalk Server, Oracle
Fusion
and webMethods. While these software packages come with a
lot of integration tools and features, they also come with a number of
drawbacks. For starters each of these tools comes with a six-figure
purchase price once you add up the costs of the software, dependencies
and hardware. They all require developers with special skills and
training to use them effectively and they are all very, some might say
needlessly, complex which makes doing even the simplest things more
difficult than they should. When all you want to do is send some
messages between two or more applications using an integration server is
really more trouble than it is worth. Luckily a number of lighter weight
messaging frameworks have appeared on the scene to help address this
problem. One of those frameworks is RabbitMQ. I chose RabbitMQ
because it was open source, freely available, cross-platform and it has
commercial support options.  For the full story on RabbitMQ, I recommend
spending some time reading through the materials on their website. The
remainder of this blog post will be about a small test project that will
send messages from a Ruby program and receive them in a .NET/C#
program.

Installing RabbitMQ

I installed the RabbitMQ server on my Fedora 14 Linux system.
Getting the server installed was a simple as using yum to install
RabbitMQ and its dependencies.

1
yum install rabbitmq-server

To start the server, become the rabbitmq user and run rabbitmq-server.
(Note: I had to become root before becoming the rabbitmq user.)

1
2
su rabbitmq
/usr/sbin/rabbitmq-server

The only gotcha I ran into required me to open TCP and UDP port 5672 to
permit my Windows virtual machine to communicate with my Linux host.

If you are interested in installing RabbitMQ on Windows I will point you
to this excellent post on Justin Etheredge’s CodeThinked blog.

Messaging with Ruby

On the Ruby side, I used the amqp gem. The Ruby program below publishes
a message to a queue, subscribes to that same message and then publishes
a second message to a different queue that the C# program will be
subscribing to.

Publisher.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
require 'mq'

AMQP.start(:host => 'localhost') do
def log *args
p [Time.now, *args]
end

amq = MQ.new
EM.add_periodic_timer(1) {
puts

log :sending, 'One!'
amq.queue('one').publish('One!')
}

amq = MQ.new
amq.queue('one').subscribe { |msg|
log 'one', :received, msg, :sending, 'Two!'
amq.queue('two').publish('Two!')
}
end

With the server running, start the program with the following command:

1
/usr/bin/ruby -rubygems publisher.rb

In the console window you will see the program start to publish messages
to the queue.

Messaging with C#

On the C# side, I used the RabbitMQ client library. The C# program
subscribes to messages from the second queue and writes them out to the
console. Compile and run this program with the server and Ruby program
running to see it pull messages from the queue.

Subscriber.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using RabbitMQ.Client;
using RabbitMQ.Client.Content;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.MessagePatterns;

public class Program
{
public static void Main(string[] args)
{
string SERVERNAME = "192.168.1.69:5672";
string QUEUENAME = "two";

var connFactory = new ConnectionFactory();
connFactory.Address = SERVERNAME;

Console.WriteLine("Connecting to broker...");
using (var conn = connFactory.CreateConnection())
{
Console.WriteLine("Creating model...");
using (var model = conn.CreateModel())
{
model.QueueBind(QUEUENAME, "amq.direct", QUEUENAME, null);

using (var subscription = new Subscription(model, QUEUENAME))
{
Console.WriteLine("Begin listening for messages...");
while (true)
{
var events = subscription.Next();

if (events != null)
{
Console.WriteLine("Event received from {0}: {1}", QUEUENAME, Encoding.UTF8.GetString(events.Body));
}
}
}
}
}
}
}

Output

Linux Console

Windows Console

Conclusion

Obviously, sending plain texts strings back and forth between programs
is not very useful. In a real messaging scenario we would need to define
a serialization format and a data contract. However, with a very small
amount of effort we managed to send messages between two different
programs running on different operating systems. It would take me longer
to install a traditional integration package than it did to develop this
solution. I will definitely be looking to do more with RabbitMQ and
other lightweight messaging frameworks in the future.