- The message producer can "fire and forget", sending the message and then moving on to more important work.
- The mechanisms required for reliability are relatively simple in the producer and consumer, and the more difficult tasks (such as persistence) can be handled in the message broker.
- It is easy to add redundancy into the messaging infrastructure
For further reference, a good book on the topic is Gregor Hohpe's Enterprise Integration Patterns
On a number of occasions I've started out with synchronous messaging, only to say to myself "I really want this to be reliable". So I fire off an http request and wait for a 200. If I don't receive a 200 after a certain amount of time, then I try again. I'll repeat this for a few times, then I'll give up, and log the failure somewhere.
Then I say to myself "It would be really nice to have this re-trying out-of-process". So I add another process that receives a request for a message to be sent, sends the message, and performs any re-trying that is required. And all of a sudden, I have asynchronous messaging. Now, there's nothing about REST that prevents asynchronous messaging, but I rarely see anyone from the Rails community talking about anything but synchronous messaging. So I thought I'd talk about how we've solved the problem on our project. We've used the ActiveMessaging plugin, originally written by some people from Thoughtworks. I'll demonstrate ActiveMessaging here, and then over the next week or two I'll contrast it with a JMS JRuby solution I've been playing around with.
Let's say we have two Rails applications - a Customer Management application and an Order Management application. The Customer application is responsible for managing all things to do with Customers - their personal details, any communications between our business and the customers, and the customer's user preferences. The Customer Management app has a rich view of a customer with fields such as:
- Telephone Number, etc.
The Order Management application needs to associate Orders with Customers, so that even when the Customer application is down for scheduled maintenance, orders can still be taken. The Orders app has a simple view of a Customer, with just a name and an id.
We will make the Customer application responsible for creating, updating and deleting Customers. Whenever any of these actions are performed, the Customer application notifies the Orders application with a message. The message indicates the type of action performed (create, update, or delete), and the id of the customer, so that the two 'views' of the Customer object (in the two different applications) can be kept in sync. The Orders application then creates, updates, or deletes its view of the customer according to the type of message that is sent.
First, we will need to setup an ActiveMQ Server. I've set it up locally on my machine using these instructions (I used the Linux instructions for my Macbook Pro):
In both of our Rails applications we will need to install the ActiveMessaging plugin:
script/plugin install http://activemessaging.googlecode.com/svn/trunk/plugins/activemessaging
ActiveMessaging provides the ability to send and receive messages. Our Customer application will send messages, and our Orders application will receive them.
For the purposes of this discussion, I will only show the 'create' messages, though the process would be very similar for 'update' and 'delete'.
Our Customer application has a Customer object that inherits from ActiveRecord::Base:
It has the following fields:
Our message will be a YAML serialized message of the following simple data object:
attr_accessor :id, :name
@id = params[:id]
@name = params[:name]
We use a rails observer to observe the 'create' event of the Customer object, construct a CustomerPayload object, serialize it, and send it via ActiveMessaging:
payload = YAML.dump(CustomerPayload.new(:id => customer.id, :name => customer.name))
publish :customer_queue, payload
To configure connection to the ActiveMQ server, I'll need the following configuration files in both of my rails applications:
ActiveMessaging::Gateway.define do |s|
s.queue :customer_queue, '/queue/Customer'
And to get the observer to work, I'll need this line in environment.rb
# Activate observers that should always be running
config.active_record.observers = :customer_observer
For consuming messages, ActiveMessaging provides a directory under app called 'processors'. In this directory of my Orders application I place my CustomerMessageProcessor:
customer_payload = YAML.load(serialized_message)
customer = Customer.new(:name => customer_payload.name)
customer.id = customer_payload.id
This class will need access to the same CustomerPayload object as the Customers application for deserialization.
In ActiveMessaging, a poller is used to poll the queue and trigger the on_message method shown above. We'll need to start the poller in the Orders application using the following command:
And that's it. If both the Orders and Customer applications are running, and have access to the ActiveMQ server, then when we create a Customer in the Customer application, it should show up in the Orders application a few moments later.