At my current employeer, we’re starting to reach the point where we need a messaging infrastructure to scale our product. The traditional Java approach is to use a JMS broker, such as ActiveMQ. However, there’s a new alternative API in the form of AMQP. Our first use-case is a fan-out queue and I took the opportunity to see how the solution would look using both API’s within Spring. You can find the code for my proof of concept on Github. I’ve only included the most relevant snippets in the post, so for a real understanding on how to configure everything, look through the source.
ActiveMQ
The heavyweight in open source Java Messaging is ActiveMQ. ActiveMQ is a traditional JMS solution. Spring provides a template class that abstracts the communication with the JMS provider. The template requires a JMS connection factory along with a destination name. The same connection factory can be used for both sending and receiving messages by classes in the same JVM. The relevant configuration bits are as follows:
This creates a destination queue, a connection factory with the host and the template for the producer. In this example, the consumer is configured as a MessageListener
and therefore, does not need a template.
The StatMessageConverter
is a bean that converts the payload to and from JSON. I choose to send the messages as JSON since we have a multi-language infrastructure and JSON makes it easier for those diverse systems to communicate.
With this configuration, sending a message with a JSON payload is as simple as:
template.convertAndSend(message);
The consumer configuration is simpler. Since my consumer class implements MessageListener
, I just need to create a listener-container
and inject the bean:
Whenever the producer sends the message, the onMessage
method of the consumer will be called by Spring.
AMQP
AMQP with Spring works much the same way. There is a template and a connection factory. However, AMQP is different than JMS. First AMQP is a wire-level protocol, whereas JMS is an API. Therefore, different AMQP implementations should be able to communicate with each other. Secondly, the paradigm is slightly different. In JMS, there is a queue and consumers and producers bind to that queue. In the AMQP world, producers talk to exchanges and consumers bind queues to those exchanges. Therefore the communication topology is fully within the configuration and is unknown to the code, making it trivial to go from a one-to-one exchange to a fan-out. The AMQP provider that made the most sense was RabbitMQ. RabbitMQ is a logical choice because it has first-party java libraries and is owned by VMWare, who also own Spring, which bodes well for the project’s future. Spring’s AMQP support requires a little more configuration compared to the JMS solution, but the basics are the same. You need connection factory, template, and a listener. The first difference is that there must be an administration bean:
Next the send template needs both the routing key and the exchange:
The statsExchange
will create the exchange as part of it’s initialization. If the exchange already exists, it will bind to it.
Next comes the creation of the Queue:
Since I want fan-out behavior, each consumer must have a unique queue name. Therefore, I use an instanceID
instead of a configured property. Otherwise, the same rule for exchanges applies here, the consumers will just listen on the existing queue.
The consumer is similar, but in this case we can use a regular Pojo. Since Spring’s AMQP support includes marshalling support for JSON, no special conversion needs to occur on the client side:
All that remains is binding the exchange to the queues:
Sending a message is identical to JMS:
template.convertAndSend(message);