Spring Cloud Stream con RabbitMQ: microservicios basados ​​en mensajes

S

Visión general

En este artículo, te presentaremos Spring Cloud Stream, que es un marco para la creación de aplicaciones de microservicio impulsadas por mensajes que están conectadas por un agente de mensajería común como RabbitMQ, Apache Kafkaetc.

Spring Cloud Stream se basa en los marcos de Spring existentes como Mensajes de Spring y Integración de Spring. Aunque estos marcos se han probado en batalla y funcionan muy bien, la implementación está estrechamente relacionada con el intermediario de mensajes utilizado. Además, a veces es difícil escalar para ciertos casos de uso.

La idea detrás de Spring Cloud Stream es un concepto de Spring Boot muy típico: hable con él en abstracción y deje que Spring descubra los detalles de implementación en tiempo de ejecución según la configuración y la gestión de dependencias. Lo que significa que puede cambiar el agente de mensajes subrayado simplemente cambiando las dependencias y el archivo de configuración. Se pueden encontrar los diversos corredores que se admiten actualmente aquí.

Usaremos RabbitMQ como agente de mensajes para este artículo. Antes de eso, repasemos algunos conceptos básicos de un corredor y por qué podemos necesitarlo en una arquitectura orientada a microservicios.

Mensajería en microservicios

En una arquitectura de microservicio, tenemos muchas aplicaciones pequeñas que se comunican entre sí para completar una solicitud; una de las principales ventajas de ellas es la escalabilidad mejorada. Es bastante común que una sola solicitud pase de más de un microservicio descendente para completarse. Por ejemplo, digamos que tenemos un Servicio-A que llama internamente al Servicio-B y al Servicio-C para completar una solicitud:

Sí, habría otros componentes como Spring Cloud Eureka, Spring Cloud Zuul y muchos más, pero estamos tratando de enfocarnos en un problema particular con este tipo de arquitectura.

Supongamos que, por alguna razón, Service-B tarda un poco más en responder. Tal vez esté haciendo una operación de E / S o una transacción de base de datos larga o llamando a diferentes servicios que requieren que sea lento de una manera que no puede hacerse más eficiente.

Ahora podemos generar más instancias de Service-B para manejar esto y está bien, pero Service-A, que en realidad es rápido, necesita esperar una respuesta del Service-B para continuar. Esto da como resultado que Service-A no pueda recibir más solicitudes, lo que significa que también tenemos que activar varias instancias de Service-A.

Otro enfoque para abordar una situación similar es tener una arquitectura de microservicio impulsada por eventos. Lo que esto significa básicamente es que en lugar de que el Servicio-A realice llamadas al Servicio-B o al Servicio-C directamente a través de HTTP, publica la solicitud o el evento en un Agente de mensajes. Service-B y Service-C serán suscriptores de este evento en el corredor de mensajes.

Esto tiene muchas ventajas sobre la arquitectura de microservicio tradicional que se basa en llamadas HTTP:

  • Mejora la escalabilidad y la confiabilidad. – Ahora sabemos qué servicios son verdaderos cuellos de botella en nuestra aplicación general.
  • Fomenta el acoplamiento flojo – El Servicio A no necesita saber sobre el Servicio-B y el Servicio-C. Todo lo que necesita es conectarse con el agente de mensajes y publicar el evento. La forma en que se orquesta el evento depende de la configuración del agente. De esta manera, el Servicio-A puede evolucionar de forma independiente, que es uno de los conceptos centrales de los microservicios.
  • Interactuar con el sistema heredado – Muy a menudo no podemos mover todo a una pila de tecnología más nueva. Todavía tenemos que trabajar con el sistema heredado que, aunque lento, es confiable.

RabbitMQ

Protocolo avanzado de Message Queue Server (AMQP) es un protocolo que usa RabbitMQ para la mensajería. Aunque RabbitMQ admite algunos otros protocolos, AMQP es más preferible debido a la compatibilidad y al gran conjunto de características que ofrece.

Diseño arquitectónico RabbitMQ

Entonces, un editor publica un mensaje en algo llamado Intercambiar en RabbitMQ. Exchange toma un mensaje y lo enruta a uno o más colas. Los algoritmos de enrutamiento dependen del tipo de intercambio y de una clave / encabezado de enrutamiento (que se transmite junto con el mensaje). Estas reglas que conectan un intercambio a una cola se denominan enlaces.

Las fijaciones pueden ser de 4 tipos:

  • Directo: Asigna directamente un tipo de intercambio a una cola específica en función de la clave de enrutamiento.
  • Fanout: Enruta los mensajes a todas las colas del intercambio vinculado.
  • Tema: Enruta los mensajes a las colas (0, 1 o más) en función de las coincidencias de claves de enrutamiento completas o una parte.
  • Encabezados: Es similar al tipo de intercambio de temas, pero se enruta según los valores de encabezado en lugar de las claves de enrutamiento.

 

Créditos – https://www.cloudamqp.com/

Esta publicación y consumo general de mensajes a través de intercambios y colas se realiza a través de un canal.

Para obtener más detalles sobre las rutas, visite este enlace.

Configuración de RabbitMQ

Instalación

Podemos descargar y configurar los binarios basados ​​en nuestro sistema operativo desde aquí.

Sin embargo, en este artículo utilizaremos una instalación gratuita basada en la nube proporcionada por cloudamqp.com. Simplemente regístrese en el servicio e inicie sesión.

En su panel principal, haga clic en “Crear nueva instancia”:

Luego, asigne un nombre a su instancia y continúe con el siguiente paso:

Luego seleccione una región:

Y por último, revise la información de su instancia y haga clic en “Crear instancia” en la esquina inferior derecha:

Eso es. Ahora tiene una instalación de RabbitMQ ejecutándose en la nube. Para obtener más detalles sobre su instancia, vaya a su panel y haga clic en la instancia recién creada:

Podemos ver el host desde el que podemos acceder a nuestra instancia de RaabbitMQ, como el nombre de usuario y la contraseña necesarios para conectarnos desde nuestro proyecto:

Usaremos “URL AMQP” en nuestra aplicación Spring para conectarnos a esta instancia, así que anótelo en alguna parte.

También puede ver la consola del administrador haciendo clic en “RabbitMQ Manager” en la esquina superior izquierda. Esto lo llevará a la administración de su instancia RabbitMQ, que se parece a lo siguiente:

Configuración del proyecto

Ahora que nuestra configuración está lista, creemos nuestros servicios:

  • productor-de-flujo-en-nube-rabbitmq: Esto actuará como un editor que enviará mensajes a RabbitMQ.
  • cloud-stream-consumidor-rabbitmq: Esto consumirá los mensajes

La mejor manera de comenzar con un proyecto esqueleto es usar Spring Initializr. Este será nuestro proyecto productor y utilizaremos puntos finales REST para publicar mensajes.

Seleccione su versión preferida de Spring Boot y agregue las dependencias “Web” y “Cloud Stream” y genere como un proyecto Maven:

Nota: Observe el mensaje entre corchetes en el cloud-stream dependencia. Dice que esto también requiere una dependencia de carpeta como RabbitMQ, Kafka, etc. para funcionar.

Como usaremos RabbitMQ, agregue la siguiente dependencia de Maven:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

Alternativamente, también podemos combinar los dos y usar Spring Cloud Stream RabbitMQ Starter:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

Del mismo modo, cree el proyecto del consumidor, pero solo con spring-cloud-starter-stream-rabbit dependencia.

Construyendo el Productor

Como dijimos anteriormente, el proceso general de transmisión de mensajes de un editor para intercambiarlos a la cola se realiza a través de un canal. Así que creemos un HelloBinding interfaz que contiene nuestro MessageChannel llamado “greetingChannel”:

interface HelloBinding {

    @Output("greetingChannel")
    MessageChannel greeting();
}

Dado que esto sería publicar el mensaje, usamos el @Output anotación. El nombre del método puede ser el que queramos y, por supuesto, podemos tener más de un canal en una única interfaz.

Ahora, creemos un punto final REST que envíe mensajes a este canal:

@RestController
public class ProducerController {

    private MessageChannel greet;

    public ProducerController(HelloBinding binding) {
        greet = binding.greeting();
    }

    @GetMapping("/greet/{name}")
    public void publish(@PathVariable String name) {
        String greeting = "Hello, " + name + "!";
        Message<String> msg = MessageBuilder.withPayload(greeting)
            .build();
        this.greet.send(msg);
    }
}

Arriba, creamos un ProducerController clase que tiene un atributo greet de tipo MessageChannel. Esto se inicializa en el constructor mediante el método que declaramos anteriormente.

Nota: También podríamos hacer lo mismo de forma compacta, pero estamos usando nombres diferentes para darle más claridad sobre cómo se conectan las cosas.

Luego tenemos un mapeo REST simple que toma un name desde el PathVariable y crea un Message de tipo String utilizando MessageBuilder. Al final, usamos el .send() método en el MessageChannel para publicar el mensaje.

Ahora, tenemos que contarle a Spring sobre nuestro HelloBinding, que haremos en nuestra clase principal usando @EnableBinding anotación:

@EnableBinding(HelloBinding.class)
@SpringBootApplication
public class Application {

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

Al final, tenemos que decirle a Spring cómo conectarse a RabbitMQ (a través de la “URL AMQP” de antes) y crear una forma de conectar “greetingChannel” a un posible consumidor.

Ambos se definen dentro del application.properties:

spring.rabbitmq.addresses=<amqp url>

spring.cloud.stream.bindings.greetingChannel.destination = greetings

server.port=8080

Construyendo al consumidor

Ahora tenemos que escuchar el canal que creamos anteriormente, es decir, “greetingChannel”. Creemos un enlace para ello:

public interface HelloBinding {

    String GREETING = "greetingChannel";

    @Input(GREETING)
    SubscribableChannel greeting();
}

Las dos diferencias con la vinculación del productor deberían ser bastante obvias. Como consumimos el mensaje, usamos SubscribableChannel y @Input anotación para conectarse a “greetingChannel” donde se enviarán los datos.

Ahora, creemos el método donde realmente procesaremos los datos:

@EnableBinding(HelloBinding.class)
public class HelloListener {

    @StreamListener(target = HelloBinding.GREETING)
    public void processHelloChannelGreeting(String msg) {
        System.out.println(msg);
    }
}

Aquí creamos una clase HelloListener que tiene un método anotado con @StreamListener, dirigido al “greetingChannel”. Este método espera un String como argumento, que acabamos de iniciar sesión en la consola. También habilitamos el HelloBinding aquí usando @EnableBinding en la parte superior de la clase.

Una vez más, usamos el @EnableBinding aquí y no la clase principal, para mostrarle que depende de usted cómo organizar los nombres, declaraciones, etc., lo que tenga más sentido para usted o su equipo.

Veamos también nuestra clase principal, que no cambiamos:

@SpringBootApplication
public class Application {

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

En application.properties necesitamos definir lo mismo que hicimos para el productor, excepto que esto se ejecutará en un puerto diferente:

spring.rabbitmq.addresses=<amqp url>
spring.cloud.stream.bindings.greetingChannel.destination=greetings
server.port=9090

Probando todo

Empecemos por el servicio al productor y al consumidor. Primero, produzcamos el mensaje presionando nuestro punto final http: // localhost: 8080 / greet / john.

En los registros del consumidor puede ver el mensaje:

Comencemos otra instancia del servicio de consumidor (en un puerto diferente) usando el siguiente comando:

$ mvn spring-boot:run -Dserver.port=9091

Ahora, cuando llegamos al punto final REST del productor para publicar, vemos que ambos consumidores recibieron el mensaje:

Esto puede ser lo que queremos en algunos de nuestros casos de uso. Pero, ¿qué pasa si solo queremos que un consumidor consuma un mensaje? Para eso, necesitamos crear un grupo de consumidores en las propiedades de la aplicación de nuestro consumidor:

spring.cloud.stream.bindings.greetingChannel.group = greetings-group

Ahora, nuevamente, ejecute 2 instancias del consumidor en diferentes puertos y verifique nuevamente publicando a través del productor:

Todo esto también se puede ver visualmente en la consola del administrador de RabbitMQ:

Conclusión

En este artículo, explicamos el concepto principal de mensajería, su función en los microservicios y cómo implementarlo utilizando Spring Cloud Stream. Usamos RabbitMQ como nuestro agente de mensajes, pero podemos usar otros agentes populares, como Kafka, simplemente cambiando la configuración y las dependencias.

Como siempre, el código de los ejemplos utilizados en este artículo se puede encontrar en GitHub

About the author

Ramiro de la Vega

Bienvenido a Pharos.sh

Soy Ramiro de la Vega, Estadounidense con raíces Españolas. Empecé a programar hace casi 20 años cuando era muy jovencito.

Espero que en mi web encuentres la inspiración y ayuda que necesitas para adentrarte en el fantástico mundo de la programación y conseguir tus objetivos por difíciles que sean.

Add comment

Sobre mi

Últimos Post

Etiquetas

Esta web utiliza cookies propias para su correcto funcionamiento. Al hacer clic en el botón Aceptar, aceptas el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad