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 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:
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.


category:
English
Deutsch 

Hi,
schöner Blog-Post – ich denke, RabbitMQ und AMQP brauchen noch viel mehr Sichtbarkeit in der Java-Welt!
Einige kleine Hinweise:
- Die SingleConnectionFactory ist für kleine Beispiele OK, aber in realen Anwendungen sollte man dann die CachingConnectionFactory nutzen.
- Es gibt noch den rabbit-XML-Namespace, der die Konfiguration vereinfacht. Die Nutzung in dem Beispiel würde nach meinem Empfinden kaum einen Vorteil bringen.
- Die beiden Test-Methoden haben eine Abhängigkeit – es muss erst queueProducer() und dann queueConsumer() ausgeführt werden. Warum nicht:
admin.declareQueue( new Queue("test.queue") );
String msg= date = "Catch the rabbit! " + date;
template.convertAndSend( msg );
Assert.assertEquals(msg, template.receiveAndConvert("test.queue"));
- Und für das Empfangen von Nachrichten sind MessageListenerContainer natürlich toll.
Habe den Test entsprechend angepasst. In der jetzigen Form ist er auch viel präziser.
Ansonsten war das Beispiel schon absichtlich so einfach wie möglich gehalten. Wer weiss, vielleicht gibt es bald einen weiteren Post zum Thema “RabbitMQ Advanced”?
—
I adjusted the test case. It’s more concise that way.
The example itself was so simple by intent. How knows, maybe we’ll have another post on advanced RabbitMQ messaging.
Hallo,
sehr schön geschriebener Artikel.
Zurzeit habe ich RabbitMQ noch nicht im einsatz, aber ich denke ich werde mal ein genaueren Blick darauf werfen.
Hi,
schöner Blog-Post, danke
Great and simple – Thanks!
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