Spring Boot - Envío y recepción de mensajería con RabbitMQ

Prerequisitos

- Tener instalado ERLANG.

- Tener instalado el servidor de RabbitMQ. Seguir la guía de instalación.

 

IMPORTANTE

Te puedes descargar el código directamente del repositorio en github o en formato zip

 

1. Configuración Maven

 <groupId>com.ledzedev</groupId>

<artifactId>ledze-messaging-rabbitmq</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>ledze-messaging-rabbitmq</name>
<description>Demo de mensajería con Spring Boot y RabbitMQ</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

2. Código Java

2.1 Receptor

/**
* Source code generated by Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 1/20/2017
*/
@Component
public class Receptor {


private static Logger log = LoggerFactory.getLogger(Receptor.class);

//Solo se agrega este contador para darle mas sentido al ejemplo, pero no se recomienda en un ambiente productivo.
private CountDownLatch latch = new CountDownLatch(1);


public void recibirMensaje(String mensaje){
log.info("Mensaje recibido: " +mensaje);
latch.countDown();
}

public CountDownLatch getLatch() {
return latch;
}

}

2.2 Clase principal donde se define la infraestructura de la mensajería (Queue, TopicExchange, Binding, Contenedor y Listener)

/**
* Source code generated by Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 1/20/2017
*/
@SpringBootApplication
public class LedzeMessagingRabbitmqApplication {


public static void main(String[] args) {
SpringApplication.run(LedzeMessagingRabbitmqApplication.class, args);
}

final static String nombreCola = "ledze-spring-boot";

/*
Spring AMQP requiere que los objetos Queue, TopicExchange y Binding sean creados como beans de Spring de alto nivel
para que sean configurados apropiadamente.
*/

@Bean
Queue cola(){

return new Queue(nombreCola, false);
}

@Bean
TopicExchange exchange(){

return new TopicExchange("ledze-spring-boot-exchange");
}

@Bean
Binding relaciona(Queue cola, TopicExchange topicExchange){

return BindingBuilder.bind(cola).to(topicExchange).with(nombreCola);
}

@Bean
SimpleMessageListenerContainer contenedor(ConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter){

SimpleMessageListenerContainer contenedor = new SimpleMessageListenerContainer();
contenedor.setConnectionFactory(connectionFactory);
contenedor.setQueueNames(nombreCola);
contenedor.setMessageListener(messageListenerAdapter);

return contenedor;
}

@Bean
MessageListenerAdapter listenerAdapter(Receptor receptor){

return new MessageListenerAdapter(receptor, "recibirMensaje");//método escuchador por defecto
}

}

3. Ejecutas la clase principal y el Listener se debe quedar esperando.

 

 

Bonus

Para probar he construido una clase para enviar los mensajes y la hacemos con un CommandLineRunner, solo para efectos de nuestro test y que se ejecute dentro del mismo contexto (ESTE TEST NO ES UNA SOLUCIÓN PARA PRODUCCIÓN).

/**
* Source code generated by Gerado Pucheta Figueroa
* Twitter: @ledzedev
* http://ledze.mx
* 1/20/2017
*/
@Component
public class LedzeMandaMensajes implements CommandLineRunner {


private static Logger log = LoggerFactory.getLogger(LedzeMandaMensajes.class);

private final RabbitTemplate rabbitTemplate;
private final Receptor receptor;
private final ConfigurableApplicationContext contexto;

public LedzeMandaMensajes(RabbitTemplate rabbitTemplate, Receptor receptor, ConfigurableApplicationContext contexto) {
this.rabbitTemplate = rabbitTemplate;
this.receptor = receptor;
this.contexto = contexto;
}

@Override
public void run(String... strings) throws Exception {

log.info("Enviando mensaje a la cola de mensajería...");
rabbitTemplate.convertAndSend("ledze-spring-boot", "Hola desde RabbitMQ");

//Solo se agrega este contador para darle mas sentido al ejemplo, pero no se recomienda en un ambiente productivo.
receptor.getLatch().await(10000, TimeUnit.MILLISECONDS);


contexto.close();
}
}

Inyectamos en la clase principal LedzeMessagingRabbitmqApplication, una instancia de la clase que manda mensajes:

@Autowired
private LedzeMandaMensajes ledzeMandaMensajes;
 

Ejecutamos nuevamente la clase principal y el resultado debe mostrar en la consola el mensaje que mandamos a la cola "Hola desde RabbitMQ".

 

 

NOTA: JMS y AMQP tienen diferente semántica. Por ejemplo: JMS manda mensajes encolados a solo un consumidor (aunque también tiene el concepto de topicos, la forma natural de funcionar de JMS es uno a uno) y AMQP manda mensajes a un exchange que por su propia naturaleza puede ir a una cola o a varias colas, funcionando de manera nativa mas como el concepto de tópicos de JMS.