Configurar Spring con RabbitMQ mediante archivos XML

![CDATA[

La integración de Spring con RabbitMQ permite enviar y recibir mensajes de forma desacoplada. En esta guía crearemos dos proyectos Maven independientes, uno para el productor y otro para el consumidor, utilizando la biblioteca spring-rabbit y configuración XML.

Dependencias comunes

Ambos proyectos necesitan las mismas dependencias en el archivo pom.xml:


<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit</artifactId>
        <version>2.1.8.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.7.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Archivo de propiedades

Ambos módulos comparten un fichero conexion.properties con los datos del bróker:


rabbitmq.host=192.168.50.20
rabbitmq.port=5672
rabbitmq.username=invitado
rabbitmq.password=invitado
rabbitmq.virtual-host=/demo

Configuración del productor

En el proyecto productor se define el contexto spring-productor-rabbit.xml con las colas e intercambiadores necesarios:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/rabbit
           http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <context:property-placeholder location="classpath:conexion.properties"/>

    <rabbit:connection-factory id="fabricaConexion"
        host="${rabbitmq.host}" port="${rabbitmq.port}"
        username="${rabbitmq.username}" password="${rabbitmq.password}"
        virtual-host="${rabbitmq.virtual-host}"/>

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

    <!-- Cola simple (default exchange) -->
    <rabbit:queue id="colaSimple" name="colaSimple" auto-declare="true"/>

    <!-- Fanout: colas e intercambiador -->
    <rabbit:queue id="colaNoticias1" name="cola.noticias1" auto-declare="true"/>
    <rabbit:queue id="colaNoticias2" name="cola.noticias2" auto-declare="true"/>
    <rabbit:fanout-exchange id="fanoutNoticias" name="fanout.noticias" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding queue="cola.noticias1"/>
            <rabbit:binding queue="cola.noticias2"/>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <!-- Topic: colas e intercambiador con patrones -->
    <rabbit:queue id="colaInfoSistema" name="cola.info.sistema" auto-declare="true"/>
    <rabbit:queue id="colaTodoSistema" name="cola.todo.sistema" auto-declare="true"/>
    <rabbit:queue id="colaTodoSistema2" name="cola.todo.sistema2" auto-declare="true"/>

    <rabbit:topic-exchange id="topicSistema" name="topic.sistema" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="sistema.*" queue="cola.info.sistema"/>
            <rabbit:binding pattern="sistema.#" queue="cola.todo.sistema"/>
            <rabbit:binding pattern="sistema.#" queue="cola.todo.sistema2"/>
        </rabbit:bindings>
    </rabbit:topic-exchange>

    <rabbit:template id="plantillaRabbit" connection-factory="fabricaConexion"/>
</beans>

Prueba del productor

La clase de test envía mensajes a las distintas colas e intercambiadores:


package com.ejemplo;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-productor-rabbit.xml")
public class ProductorPrueba {

    @Autowired
    private RabbitTemplate plantillaRabbit;

    @Test
    public void enviarAColaSimple() {
        plantillaRabbit.convertAndSend("colaSimple", "Mensaje simple");
    }

    @Test
    public void enviarFanout() {
        plantillaRabbit.convertAndSend("fanout.noticias", "", "Noticia urgente");
    }

    @Test
    public void enviarTopic() {
        plantillaRabbit.convertAndSend("topic.sistema", "sistema.alerta", "Alerta del sistema");
    }
}

Configuración del consuimdor

El proyecto consumidor declara los listeners y los asocia con las colas en spring-consumidor-rabbit.xml:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/rabbit
           http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <context:property-placeholder location="classpath:conexion.properties"/>

    <rabbit:connection-factory id="fabricaConexion"
        host="${rabbitmq.host}" port="${rabbitmq.port}"
        username="${rabbitmq.username}" password="${rabbitmq.password}"
        virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="oyenteSimple" class="com.ejemplo.rabbit.listeners.OyenteSimple"/>
    <bean id="oyenteNoticias1" class="com.ejemplo.rabbit.listeners.OyenteNoticias1"/>
    <bean id="oyenteNoticias2" class="com.ejemplo.rabbit.listeners.OyenteNoticias2"/>
    <bean id="oyenteInfoSistema" class="com.ejemplo.rabbit.listeners.OyenteInfoSistema"/>
    <bean id="oyenteTodoSistema" class="com.ejemplo.rabbit.listeners.OyenteTodoSistema"/>
    <bean id="oyenteTodoSistema2" class="com.ejemplo.rabbit.listeners.OyenteTodoSistema2"/>

    <rabbit:listener-container connection-factory="fabricaConexion" auto-declare="true">
        <rabbit:listener ref="oyenteSimple" queue-names="colaSimple"/>
        <rabbit:listener ref="oyenteNoticias1" queue-names="cola.noticias1"/>
        <rabbit:listener ref="oyenteNoticias2" queue-names="cola.noticias2"/>
        <rabbit:listener ref="oyenteInfoSistema" queue-names="cola.info.sistema"/>
        <rabbit:listener ref="oyenteTodoSistema" queue-names="cola.todo.sistema"/>
        <rabbit:listener ref="oyenteTodoSistema2" queue-names="cola.todo.sistema2"/>
    </rabbit:listener-container>
</beans>

Implementación de los listeners

Cada oyente procesa el mansaje recibido. Ejemplo de OyenteSimple:


package com.ejemplo.rabbit.listeners;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class OyenteSimple implements MessageListener {
    @Override
    public void onMessage(Message message) {
        System.out.println("OyenteSimple: " + new String(message.getBody()));
    }
}

Los demás oyentes siguen la misma estructura, mostrando un mensaje identificador en la consola.

Arranque del consumidor

El consumidor permanece a la escucha mediante un bucle infinito en la prueba:


package com.ejemplo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-consumidor-rabbit.xml")
public class ConsumidorPrueba {

    @Test
    public void mantenerEscucha() {
        while (true) {
            // el listener-container gestiona la recepción en segundo plano
        }
    }
}

Al ejecutar este test y luego lanzar los métodos del productor, los mansajes aparecerán en la consola del consumidor.

]]>

Etiquetas: Spring RabbitMQ spring-amqp xml-config fanout-exchange

Publicado el 10-15 18:36