coderberry

Bunny RabbitMQ

Recently I was asked to implement Redis/Resque into an existing project that already had Redis up and running with another Redis server. This ended up being a lot more difficult than I had anticipated due to the singleton nature of the Resque gem. This led us to explore other possible options and we ended up on RabbitMQ.

RabbitMQ has two heavily supported gems that can be used to interface with our Rails app. One of them is the AMQP gem. It is very powerful and acts as a daemon for both publisher and consumer. Our needs aren’t as big, however, so we decided to use the bunny gem.

Initially, I started off by wanting to create a publisher/consumer scenario. I installed RabbitMQ on a VirtualBox using Vagrant.

1
2
3
$ gem install vagrant # See vangrantup.com for install instructions
$ vagrant box add base http://files.vagrantup.com/lucid32.box
$ vagrant init

Make sure you modify your Vagrantfile to allow port access for the tcp protocol:

1
config.vm.forward_port "tcp", 5672, 5672

Then I started up the server, logged in and installed the RabbitMQ server:

$ vagrant up
$ vagrant ssh
[email protected]:~$ sudo apt-get install rabbitmq-server
...

Once this is done, the RabbitMQ server starts automatically. To ensure that it’s working you can run the following command. Remember to run it as sudo.

1
2
3
4
vagrant@lucid32:~$ sudo rabbitmqctl list_queues
Listing queues ...
...done.
vagrant@lucid32:~$

Now that this works, let’s play with the gem. Ensure that you have the bunny gem installed.

1
$ gem install bunny

Once this is installed, start up a console and we will create a queue.

1
2
3
4
5
6
7
8
9
10
11
$ irb
> require 'rubygems'
 => true
> require 'bunny'
 => true
> my_client = Bunny.new
 => #<Bunny::Client:0x1095a..., @port=5672>
> my_client.start
 => :connected
> my_queue = my_client.queue('my_first_queue')
 => #<Bunny::Queue:0x1063v..., @port=5672>>

Now we have a queue created. Let’s go back to our RabbitMQ server and run the list_queues command again.

1
2
3
4
5
vagrant@lucid32:~$ sudo rabbitmqctl list_queues
Listing queues ...
my_first_queue  0
...done.
vagrant@lucid32:~$

As you can see, we now have the my_first_queue queue created with 0 items in the queue.

Let’s now add a message to the queue.

1
2
3
4
5
> direct_exchange = my_client.exchange('')
> direct_exchange.publish('This is my first message', :key => my_queue.name
 => nil
> my_queue.message_count
 => 1

And now when you run the list_queues command, you will see it has a 1 after the my_first_queue.

1
2
3
4
5
vagrant@lucid32:~$ sudo rabbitmqctl list_queues
Listing queues ...
my_first_queue  1
...done.
vagrant@lucid32:~$

Awesome. Now that we know how to add messages into our RabbitMQ server using the bunny gem, let’s now create a consumer to read the messages from the queue.

Let’s open up another terminal and run irb. This part will be the same as before.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ irb
> require 'rubygems'
 => true
> require 'bunny'
 => true
> my_client = Bunny.new
 => #<Bunny::Client:0x1095a..., @port=5672>
> my_client.start
 => :connected
> my_queue = my_client.queue('my_first_queue')
 => #<Bunny::Queue:0x1063v..., @port=5672>>
 ...
> my_queue.message_count
 => 1

To grab the next item in the queue, we would call pop on my_queue

1
2
3
4
> msg = my_queue.pop[:payload]
 => "This is a test message"
> my_queue.message_count
 => 0

If we want it to run as a listener, we can place the pop in a loop.

1
2
3
4
5
6
7
8
> loop do
>   if my_queue.message_count > 0
>     msg = my_queue.pop[:payload]
>     puts "Found Message: #{msg}"
>   else
>     sleep 5
>   end
> end

There you have it!

Comments