Overview

AMQP Messaging with RabbitMQ and Spring

6 Comments

RabbitMQ is the message broker of the vFabric Cloud Application Platform. Its support of the performant messaging protocol standard AMQP makes RabbitMQ a perfect match for high availability scenarios. RabbitMQ is open source and can be used outside the vFabric platform. Commercial Support is availabe on demand.

We are going to show how you can use Spring AMQP to integrate a RabbitMQ broker with your Java application.

Erlang/OTP Installation

RabbitMQ is implemented in Erlang. Since Erlang requires its own runtime, first of all we have to install the Erlang/OTP runtime (Open Telecom Platform). Pick release R14B02 for the Windows platform from the download page. We choose the following installation folder C:\erl5.8.3 and define an environment variable pointing to that folder:

ERLANG_HOME=C:\erl5.8.3

RabbitMQ Installation

After downloading RabbitMQ we extract the ZIP to C:\rabbitmq_server-2.4.1. RabbitMQ is started with the following script:

C:\rabbitmq_server-2.4.1\sbin\rabbitmq-server.bat
RabbitMQ Server

RabbitMQ Server

RabbitMQ features a small initial memory footprint and has a short ramp-up time – two advantages for elastic cloud environments. Client APIs are offered for several languages including Java and .NET.

Spring AMQP

Spring AMQP offers an API for an easy access to AMQP message brokers. As usual, a Spring template serves as an abstraction from technical details. For AMQP, AmqpTemplate does the job.

The dependencies of the involved Spring projects are shown in the following figure:

Spring AMQP API

Spring AMQP API

The spring-amqp project holds all essential general interfaces (e.g. the AmqpTemplate) and API classes, while the broker specific implementation goes into spring-rabbitmq which in turn relies on the general Java API for RabbitMQ amqp-client.

In an ideal world, your client application only depends on spring-amqp to achieve a loose coupling. This enables you to switch from one AMQP broker to another without any major changes in the code.

In our code examples we use the following Maven dependencies:

...
<repositories>
	<repository>
		<id>repository.springframework.maven.milestone</id>
		<name>Spring Framework Maven Milestone Repository</name>
		<url>http://maven.springframework.org/milestone</url>
	</repository>
</repositories>
<properties>
	<spring.framework.version>3.0.5.RELEASE</spring.framework.version>
	<spring.amqp.version>1.0.0.M3</spring.amqp.version>
	<rabbitmq.version>2.2.0</rabbitmq.version>
</properties>
<dependencies>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-core</artifactId>
		<version>${spring.framework.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.amqp</groupId>
		<artifactId>spring-amqp</artifactId>
		<version>${spring.amqp.version}</version>
		<exclusions>
			<exclusion>
				<groupId>com.sun.jmx</groupId>
				<artifactId>jmxri</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.springframework.amqp</groupId>
		<artifactId>spring-rabbit</artifactId>
		<version>${spring.amqp.version}</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.amqp</groupId>
		<artifactId>spring-erlang</artifactId>
		<version>${spring.amqp.version}</version>
	</dependency>
	<dependency>
		<groupId>com.rabbitmq</groupId>
		<artifactId>amqp-client</artifactId>
		<version>${rabbitmq.version}</version>
	</dependency>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.7</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>${spring.framework.version}</version>
		<scope>test</scope>
	</dependency>
</dependencies>
...

AMQP Template

To keep things simple we are using a JUnit test with context configuration. The application context holds a connection factory and the AmqpTemplate. For admin purposes, we add another bean.

<!-- Connection Factory -->
<bean id="rabbitConnFactory" 
	class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory">
	<constructor-arg><value>localhost</value></constructor-arg>
	<property name="username" value="guest" />
	<property name="password" value="guest" />
	<property name="virtualHost" value="/" />
	<property name="port" value="5672" />
</bean>
 
<!-- Spring AMQP Template -->
<bean id="template" 
	class="org.springframework.amqp.rabbit.core.RabbitTemplate">
	<property name="connectionFactory" ref="rabbitConnFactory" />
	<property name="routingKey" value="test.queue"/>
	<property name="queue" value="test.queue"/>
</bean>
 
<!-- Spring AMQP Admin -->
<bean id="admin" class="org.springframework.amqp.rabbit.core.RabbitAdmin">
	<constructor-arg ref="rabbitConnFactory" />
</bean>

The connectory factory basically needs to be configured with the TCP/IP connection parameters to locate the RabbitMQ broker. We use the default port 5672 and the credentials guest/guest.

The template is configured to use a queue named test.queue.

Our example uses autowiring since we configured exactly one implementation. Thus
AmqpAdmin and AmqpTemplate are injected like this:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class RabbitMQClientTest {
 
	@Autowired private AmqpAdmin admin;
	@Autowired private AmqpTemplate template;
 
	@Test public void simpleProducerConsumerTest() {
		try {
			String sent = "Catch the rabbit! " + new Date();
			admin.declareQueue( new Queue("test.queue") );
 
			// write message
			template.convertAndSend( sent );
			// read message
			String received = (String)template.receiveAndConvert();
 
			System.out.println( "Msg: " + received );
			Assert.assertEquals( sent, received );
 
		} catch (AmqpException e) {
			Assert.fail( "Test failed: " + e.getLocalizedMessage() );
		}
	}
}

First we use AmqpAdmin to declare the queue test.queue. This operation is idempotent, i.e. the queue is created only if it doesn’t exist.

After that convertAndSend(...) can be used to easily send any object over the wire. Since AMQP’s message payload is basically a byte array, the AmqpTemplate performs a conversion under the hood, as long as you do not configure your custom MessageConverter. For our purposes the standard conversion is sufficient, because both message producer and consumer are pure Java.

Finally we use receiveAndConvert(...) to perform a synchronous read on the queue and print out the String representation of the message.

The AmqpException is a RuntimeException, so it would not be neccessary to catch it. Since we are ideal testers we catch it anyway.

Roundup

We guided you through the installation of RabbitMQ and the neccessary Erlang/OTP runtime. After a short introduction to Spring AMQP we used the AmqpTemplate to act an as message producer and consumer.

Kommentare

  • Hi,
    schöner Blog-Post, danke

  • 5. May 2011 von jumar

    Great and simple – Thanks!

  • 27. May 2012 von Haza

    Hi,
    Spring 3.1.1 not supported.Why?

    • hi , haza
      because spring-erlang declared to required older spring-bean.(my is 3.0.5)
      this cause spring-core 3.1.1 cannot find some new method.
      you must exclude spring-bean from spring-erlang

  • 18. July 2014 von Madhan sekaran

    Thanks for a simple working example.The default port for RabbitMQ is 15672 while the port specified is 5672.This works correctly.How?

Comment

Your email address will not be published. Required fields are marked *