RabbitMQ and Rails

If you have a Rails application and want to start publishing messages to a RabbitMQ instance then you may be considering using Bunny. Getting started with the default configuration is very simple but fine tuning your setup for a production environment deserves more attention as each setting may greatly impact the eco system.

There are many different ways to implement a publisher in rails. I will highlight one method that I have used with success that utilizes a singleton class.

Setup

Many examples you will find online open and close a connection for each published message. This works well, especially for small scale applications and when the servers are in proximity of each-other. Although, for high volume message passing it is not an ideal solution. I don’t want to initiate a new connection with RabbitMQ, create a channel, send the message, and then close the connection every time I publish.

Another approach is to create the connection on startup. That leaves you with one connection and one channel. The downside is you have to manage the connection and ensure it is always up and running.

The method I describe below is a hybrid of the two. I lazy load the connection and channel. The first time we publish a message, we will setup the connection and leave it open. If we publish again then we use that connection. Prior to accessing the connection we check its status. For any reason the connection or channel is closed then we create a new one.

Connection Manager

Create our ConnectionManager singleton class.

class ConnectionManager
  include Singleton

  def initialize
    establish_connection
  end
end

Establish Connection

We establish the connection and create its channel.

attr_accessor :active_connection, :active_channel

def establish_connection
  @active_connection = Bunny.new
  active_connection.start
  @active_channel = active_connection.create_channel

  @active_connection
end

Accessing the Connection and Channel

A connection might close overtime. Let’s check its health each time we access the channel or connection.


def connection
  return active_connection if connected?
  establish_connection

  active_connection
end

def channel
  return active_channel if connected? && active_channel&.open?
  establish_connection

  active_channel
end

def connected?
  active_connection&.connected?
end

Publisher

Add our publisher.

  class Publisher
    DEFAULT_OPTIONS = { durable: true, auto_delete: false }.freeze

    def self.publish(queue_name:, payload:)
      channel = ConnectionManager.instance.channel
      queue = channel.queue(queue_name, DEFAULT_OPTIONS)
      queue.publish(payload, routing_key: queue.name)
    end
  end

Now we can publish.

Publisher.publish(queue_name: "greetings", payload: { hello: :world })

Useful Resources

 

Did you like this article? Check out these too.


 

Found this useful? Have a suggestion? Get in touch at blog@hocnest.com.