1. Introducción


Bienvenidos a un nuevo post de nuestro querido blog!. En esta ocasión veremos cómo instalar RabbitMQ en Docker y cómo escribir y leer mensajes de una cola.

Os podéis descargar el código de ejemplo de mi GitHub aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • SpringBoot 1.5.2.RELEASE
  • Spring 4.3.7.RELEASE
  • RabbitMQ 3.6.9
  • Erlang 19.3

2. Instalación de RabbitMQ en Docker

Como viene siendo costumbre en este blog utilizaremos Docker para instalar el software adicional que vamos necesitando. Si no estás muy familiarizado con esta nueva tecnología puedes ver el post en donde hablamos de qué es Docker y para qué lo utilizamos.

Especificamos en nuestro fichero docker-compose.yml que queremos arrancar un contenedor de la imagen rabbitmq donde se configure el virtualhost / para el usuario admin con la password admin.

rabbit1:
  image: "rabbitmq"
  environment:
    RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG"
    RABBITMQ_DEFAULT_USER: "admin"
    RABBITMQ_DEFAULT_PASS: "admin"
    RABBITMQ_DEFAULT_VHOST: "/"
  ports:
    - "15672:15672"
    - "5672:5672"
  volumes:
    - "./enabled_plugins:/etc/rabbitmq/enabled_plugins"

Además montamos en el directorio /etc/rabbitmq/ el fichero enabled_plugins donde establecemos que queremos configurar los plugins de gestión web.

[rabbitmq_management, rabbitmq_management_visualiser].

Arrancamos nuestro contenedor

docker-compose up

Vamos a la url http://localhost:15672

3. Configurando microservicio SpringBoot

Dependencia y Main

Para conectarnos a RabbitMQ es necesario añadir la dependencia org.springframework.boot:spring-boot-starter-amqp a nuestro build.gradle

group 'org.hernandez.ramirez.jorge.pruebaconcepto'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springboot.rabbitmq.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-amqp")
}

Main que arranca Spring Boot

package com.jorgehernandezramirez.spring.springboot.rabbitmq;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

	public Application(){
		//Para Spring
	}

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

Configuración

Para configurar la conexión a RabbitMQ debemos crear los siguientes beans.

    @Bean
    public ConnectionFactory connectionFactory() throws Exception {
        final CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(user);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualhost);
        return connectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate() throws Exception {
        return new RabbitTemplate(connectionFactory());
    }

Hasta ahora no hemos creado ninguna cola dentro de nuestro contenedor de RabbitMQ. Esto lo haremos desde el arranque del contexto de Spring. Crearemos

  • Exchange
  • Queue
  • Vincularemos Exchange al Queue

Si la cola y el exchange no existen se crea. Si existe no se hace nada.

    @Bean
    public Queue queue() {
        return new Queue(QUEUE_NAME);
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange(EXCHANGE_NAME);
    }

    @Bean
    public Binding binding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
    }

Mostramos todo en el fichero RabbitConfiguration.

package com.jorgehernandezramirez.spring.springboot.rabbitmq.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfiguration {

    public static final String QUEUE_NAME = "test-queue";

    private static final String EXCHANGE_NAME = "test-queue-exchange";

    @Value("${rabbitmq.host}")
    private String host;

    @Value("${rabbitmq.port}")
    private Integer port;

    @Value("${rabbitmq.user}")
    private String user;

    @Value("${rabbitmq.password}")
    private String password;

    @Value("${rabbitmq.virtualhost}")
    private String virtualhost;

    @Bean
    public ConnectionFactory connectionFactory() throws Exception {
        final CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setAddresses(host);
        connectionFactory.setPort(port);
        connectionFactory.setUsername(user);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualhost);
        return connectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate() throws Exception {
        return new RabbitTemplate(connectionFactory());
    }

    @Bean
    public Queue queue() {
        return new Queue(QUEUE_NAME);
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange(EXCHANGE_NAME);
    }

    @Bean
    public Binding binding(Queue queue, TopicExchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(QUEUE_NAME);
    }
}
rabbitmq:
   host: localhost
   port: 5672
   user: admin
   password: admin
   virtualhost: /

Escribiendo y leyendo de la cola

Para escribir en la cola deberemos hacer uso del objeto de la clase RabbitTemplate

rabbitTemplate.convertAndSend(RabbitConfiguration.QUEUE_NAME, MESSAGE)

Para leer de la cola deberemos utilizar la anotación @RabbitListener

@RabbitListener(queues = RabbitConfiguration.QUEUE_NAME)
public void onMessageFromRabbitMQ(final String messageFromRabbitMQ){
   LOGGER.info("{}", messageFromRabbitMQ);
}

Se muestra el controlador que escribe y lee de la cola

package com.jorgehernandezramirez.spring.springboot.rabbitmq.controller;

import com.jorgehernandezramirez.spring.springboot.rabbitmq.config.RabbitConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class);

    private static final String MESSAGE = "Message to RabbitMQ!";

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public TestController(){
        //Para Spring
    }

    @RequestMapping("/")
    public String doSendMessageToRabbitMQ() {
        rabbitTemplate.convertAndSend(RabbitConfiguration.QUEUE_NAME, MESSAGE);
        return "Alive!";
    }

    @RabbitListener(queues = RabbitConfiguration.QUEUE_NAME)
    public void onMessageFromRabbitMQ(final String messageFromRabbitMQ){
        LOGGER.info("{}", messageFromRabbitMQ);
    }
}

3. Testeando la aplicación

A continuación compilamos nuestros fuentes y arrancamos SpringBoot para testear nuestros controladores

Compilamos

gradle clean build

Arrancamos la aplicación

java -jar [JAR_EN_BUILD_LIBS]

Atacamos al controlador que inserta un mensaje en la cola

curl http://localhost:8080

Se imprime por consola.

Message to RabbitMQ!