Spring Cloud Stream con RabbitMQ: microservicios basados 鈥嬧媏n mensajes

    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 鈥嬧媏n 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

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *