Step 1: Add required Gems

In order to subscribe to our RabbitMQ queue, we will be using the bunny library, inside a process which has been daemonized using the daemons library.

While evaluating libraries to handle the RabbitMQ subscription, there appeared to be two popular gems: bunny and amqp . While both have incredibly similar interfaces, we were unable to connect to our feed using amqp , despite using the same configuration for both.

Once these libraries have been installed via bundle install , we can then begin to create our daemonized RabbitMQ process.

Step 2: Process Setup

The Gist below shows the basic setup that is required to define a daemonized Ruby process which can be run on Heroku, and which retains all the standard behavior of a Rails process, such as logging and a database connection.

Note the use of the multiple keyword which is used to prevent multiple instances of this daemon from executing at the same time. Also note, that for the purposes of this post, rabbit_mq_handler.rb is located at RAILS_ROOT/lib/rabbit_mq_handler.rb , however, if you are defining it elsewhere, please ensure to update all require s and require_relative s.

There are several bits of code in the Gist that are required to ensure that our daemonized process can log correctly and has an accessible connection to the database:

When the daemonized process is starting, we use collect_open_files to record all live files that are open before the daemonized process has been run, including the processes log file, inside the garbage collector. Once our daemonized process is run, we then use reopen_initial_files to open new streams associated with these files.

When the daemonized process is starting, we also use establish_active_record_connection to ensure that we have a connection to the database available. Note: this is not necessary if you do not plan on interacting with the database.

Step 3: Subscribe to RabbitMQ

Once we have our process setup to run correctly locally and in production, we can then handle the subscription to the RabbitMQ queue and any publications that are pushed to that queue. We have defined our subscription handling functions inside RabbitMqHelper .

In our example, both our exchange and queue are marked as durable, however this may not be correct for other RabbitMQ servers. Update the initialization of the exchange and queue variables to suit the configuration of your RabbitMQ server.

Note that we have used a blocking subscription to the queue via the block keyword, as we do not want the process handling the subscription to terminate. If you wanted to just handle any pending publications to the queue without waiting for future publications, you would not need to use a blocking subscription and could allow the process to exit, although such an approach would be better suited to a periodic Rake task.

Also note that we are expecting our payload to contain JSON, hence the call to JSON.parse . payload , when received from RabbitMQ, is a String and may not contain JSON depending on your use case.

We have enabled manual delivery acknowledgements via the manual_ack keyword, as this gives us fine grained control of whether or not we acknowledge receipt of deliver. We can then base our acknowledgements upon our processing (though in the example above, we just acknowledge all deliveries). This can be useful, for example, if we don’t want to acknowledge delivery of anything that causes our processing to fail.

Step 4: Putting it Together

Once we have defined our RabbitMQ helper, all that remains is to include our RabbitMqHelper and call handle_subscription inside our daemon.

Step 5: Starting Daemonized Process via Procfile

For the purposes of this example, I have added rabbit_mq_handler.rb to RAILS_ROOT/lib/rabbit_mq_handler.rb . In order for this to be registered as a process on Heroku, we must add it to our Profile . As it is a daemonized process, we must also tell it to run .

The only thing that is left to enable to process from the Heroku resource settings or the Heroku CLI.