TDC 2012 – Java e AMQP: Uma alternativa ao JMS

Durante o TDC desse ano, dei uma palestra sobre Java e AMQP, no lado B da trilha de Java EE. Infelizmente, na hora de mostrar uma aplicação integrando Java com o Rabbit MQ, o notebook com Ubuntu travou e não teve jeito, provavelmente por algum problema com o driver da placa de vídeo ou algo do tipo. 😦
Esse post é para apresentar o código que eu fiquei “devendo” durante a palestra. Lembrando que, para poder executá-lo, antes é necessário instalar o Rabbit MQ. Em seu site, existe o procedimento para instalação em diversas plataformas.

Aplicação

A aplicação é bem simples e é consituída de uma classe que envia e outra que recebe mensagens via AMQP, além de uma classe de testes para chamar a classe de envio. Como o projeto é baseado no spring, teremos 3 arquivos de configuração, sendo 1 com a parte genérica (declaração de exchange, queue e binding), 1 com a configuração do sender e outro para o consumer.
Como ferramenta de build, como sempre, está sendo utilizado o maven. O pom do projeto ficou da seguinte maneira:

pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>br.com.tdc.javaee</groupId>
	<artifactId>javaamqp</artifactId>
	<version>1.0.0</version>

	<properties>
		<spring.version>3.1.0.RELEASE</spring.version>
		<spring-amqp.version>1.1.1.RELEASE</spring-amqp.version>
		<amqp-client.version>2.8.1</amqp-client.version>
		<log4j.version>1.2.17</log4j.version>
		<cglib.version>2.2</cglib.version>
		<junit.version>4.8.2</junit.version>
	</properties>

	<repositories>
		<repository>
			<id>spring-milestone</id>
			<name>Spring Maven MILESTONE Repository</name>
			<url>http://maven.springframework.org/milestone</url>
		</repository>
	</repositories>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<dependencies>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit</artifactId>
			<version>${spring-amqp.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-amqp</artifactId>
			<version>${spring-amqp.version}</version>
		</dependency>
		<dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>${amqp-client.version}</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>${log4j.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib-nodep</artifactId>
			<version>${cglib.version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

</project>

Arquivos de configuração do Spring.

applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Arquivo com as configuracoes principais do Rabbit MQ -->

	<!-- define o connection factory -->
	<rabbit:connection-factory id="connectionFactory"
		host="localhost" username="guest" password="guest" port="5672"
		channel-cache-size="10" />

	<!-- define o exchange a ser criado, caso ainda nao exista -->
	<rabbit:direct-exchange name="exchange.tdc">
		<!-- faz o binding do exchange com uma fila -->
		<rabbit:bindings>
			<rabbit:binding queue="test.queue" key="test.queue" />
		</rabbit:bindings>
	</rabbit:direct-exchange>

	<!-- cria a fila, caso ainda nao exista -->
	<rabbit:queue name="test.queue" />

	<rabbit:admin connection-factory="connectionFactory" />

</beans>
applicationContext-sender.xml
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Arquivo com as configuracoes do enviador da fila de mensagens via Rabbit MQ -->

	<!-- Importa o arquivo com as configuracoes principais do Rabbit MQ -->
	<import resource="applicationContext.xml" />

	<context:annotation-config />

	<context:component-scan base-package="br.com.tdc.javaee.javaamqp.sender" />

	<!-- configura o template usado para enviar mensagens -->
	<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
		queue="test.queue" routing-key="test.queue" exchange="exchange.tdc" />

</beans>
applicationContext-consumer.xml
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

	<!-- Arquivo com as configuracoes do consumidor da fila de mensagens via Rabbit MQ -->

	<!-- Importa o arquivo com as configuracoes principais do Rabbit MQ -->
	<import resource="applicationContext.xml" />

	<context:annotation-config />

	<context:component-scan base-package="br.com.tdc.javaee.javaamqp.consumer" />

	<!-- Define o consumidor da fila, com ate 10 consumidores concorrentes -->
	<rabbit:listener-container
		connection-factory="connectionFactory" concurrency="10">
		<rabbit:listener ref="consumer" queue-names="test.queue" />
	</rabbit:listener-container>

</beans>

Consumidor

A classe consumidora de mensagens é um bean do Spring, com um método main para que ela possa ser executada de forma standalone. Ela implementa a interface org.springframework.amqp.core.MessageListener e sobreescreve o método onMessage(), que é chamado toda vez que chega uma nova mensagem na fila.

Consumer.java
package br.com.tdc.javaee.javaamqp.consumer;

import org.apache.log4j.Logger;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class Consumer implements MessageListener {

	private Logger logger = Logger.getLogger(Consumer.class);

	public static void main(String[] args) {
		new ClassPathXmlApplicationContext("/applicationContext-consumer.xml");
	}

	@Override
	public void onMessage(Message message) {
		String msg = new String(message.getBody());
		logger.info("----------Recebeu msg " + msg);
	}

}

Produtor

A classe que publica mensagens na fila é ainda mais simples, pois quase todo o trabalho é feito pela classe org.springframework.amqp.core.AmqpTemplate, injetada automaticamente pelo Spring.

AMQPSender.java
package br.com.tdc.javaee.javaamqp.sender;

import org.apache.log4j.Logger;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AMQPSender {

	@Autowired
	private AmqpTemplate amqpTemplate;

	private Logger logger = Logger.getLogger(AMQPSender.class);

	public void sendMessage(String message) {
		logger.info("Enviando mensagem " + message);
		amqpTemplate.convertAndSend(message.getBytes());
	}

}

Classe de teste

Para finalizar, foi criada uma simples classe com JUnit que chama a classe AMQPSender para fazer o envio de mensagens.

TestAMQPSender.java
package br.com.tdc.javaee.javaamqp.sender;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import br.com.tdc.javaee.javaamqp.sender.AMQPSender;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext-sender.xml")
public class TestAMQPSender {

	@Autowired
	private AMQPSender amqpSender;

	@Test
	public void testSend() {
		amqpSender.sendMessage("Test Message");
	}

}

Executando a aplicação

Primeiramente execute a classe Consumer normalmente. Pelo Eclipse, basta executar como “Java Application”. Na primeira execução, o Spring irá criar os elementos no Rabbit MQ, como o exchange, a queue e o binding entre o exchange e o queue. Essa classe ficará rodando “eternamente”, esperando e recebendo as mensagens que vão chegando na fila.
Para fazer o envio de mensagens e ver o consumidor recebendo-as, basta executar a classe de teste TestAMQPSender como “JUnit Test”.

Informações

O post de hoje foi bem mais objetivo e com menos explicação, pelo fato de ter mais informações na palestra.
O código do projeto pode ser obtido no github. A apresentação pode ser vista no
slideshare. O site do RabbitMQ possui bastante informação útil e vale a pena dar uma olhada.
Qualquer dúvida, é só usar o espaço para comentários. Abraços.


Quer aprender muito mais? Não deixe de ver meu curso Construa uma aplicação do zero com JEE 7, Java 8 e Wildfly.

Anúncios
Esta entrada foi publicada em Java EE, Spring com as etiquetas , , , , . ligação permanente.

Uma resposta a TDC 2012 – Java e AMQP: Uma alternativa ao JMS

  1. Pingback: Processamento assíncrono com ExecutorServices | Software, Java e Arquitetura

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s