Cola de mensajes en Node.js con AWS SQS

    Introducción

    Con el aumento de la complejidad de los sistemas de software modernos, surgió la necesidad de dividir los sistemas que habían superado su tamaño inicial. Este aumento en la complejidad de los sistemas hizo que fuera más difícil mantenerlos, actualizarlos y mejorarlos.

    Esto allanó el camino para los microservicios que permitieron que los sistemas monolíticos masivos se dividieran en servicios más pequeños que están débilmente acoplados pero que interactúan para brindar la funcionalidad total de la solución monolítica inicial. El acoplamiento suelto proporciona agilidad y facilita el proceso de mantenimiento y la adición de nuevas funciones sin tener que modificar sistemas completos.

    Es en estas arquitecturas de microservicios donde los sistemas de colas resultan útiles para facilitar la comunicación entre los servicios separados que componen la configuración completa.

    En esta publicación, nos sumergiremos en los sistemas de colas, en particular el Simple Queue Service de Amazon, y demostraremos cómo podemos aprovechar sus características en un entorno de microservicio.

    ¿Qué es Message Queue Server?

    Antes de que Internet y el correo electrónico entraran en escena, las personas que vivían a largas distancias se comunicaban principalmente a través del intercambio de cartas. Las cartas contenían los mensajes que se compartirían y se publicaron en la estación de correos local desde donde serían transferidas a la dirección del destinatario.

    Esto podría haber diferido de una región a otra, pero la idea era la misma. La gente confiaba en intermediarios para que les transmitieran sus mensajes a medida que avanzaban con sus vidas.

    Cuando un sistema se divide en componentes o servicios más pequeños que se espera que funcionen juntos, necesitarán comunicarse y pasar información de un servicio a otro, según la funcionalidad de los servicios individuales.

    La cola de mensajes facilita este proceso al actuar como el «servicio de oficina postal» para los microservicios. Los mensajes se colocan en una cola y los servicios de destino recogen y actúan sobre los que se dirigen a ellos. Los mensajes pueden contener cualquier cosa, como instrucciones sobre los pasos a seguir, los datos sobre los que actuar o guardar, o trabajos asincrónicos a realizar.

    La cola de mensajes es un mecanismo que permite que los componentes de un sistema se comuniquen e intercambien información de manera asincrónica. Esto significa que los sistemas débilmente acoplados no tienen que esperar comentarios inmediatos sobre los mensajes que envían y pueden liberarse para continuar manejando otras solicitudes. Cuando llega el momento y se requiere la respuesta, el servicio puede buscar la respuesta en la cola de mensajes.

    A continuación, se muestran algunos ejemplos de colas de mensajes o intermediarios populares:

    Casos de uso de Message Queue Server

    Las colas de mensajes no son necesarias para todos los sistemas, pero existen ciertos escenarios en los que vale la pena el esfuerzo y los recursos necesarios para configurarlas y mantenerlas. Cuando se utilizan apropiadamente, las colas de mensajes son ventajosas de varias formas.

    Primero, las colas de mensajes apoyan el desacoplamiento de grandes sistemas al proporcionar el mecanismo de comunicación en un sistema débilmente acoplado.

    La redundancia se refuerza mediante el uso de colas de mensajes al mantener el estado en caso de que falle un servicio. Cuando un servicio fallido o defectuoso reanuda las operaciones, todas las operaciones que debía manejar seguirán en la cola y puede recogerlas y continuar con las transacciones, que de otro modo podrían haberse perdido.

    La cola de mensajes facilita el procesamiento por lotes de operaciones, como enviar correos electrónicos o insertar registros en una base de datos. Las instrucciones por lotes pueden guardarse en una cola y procesarse todas al mismo tiempo en orden en lugar de procesarse una por una, lo que puede ser ineficaz.

    Los sistemas de colas también pueden ser útiles para garantizar la coherencia de las operaciones al garantizar que se ejecuten en el orden en que se recibieron. Esto es especialmente importante cuando se han replicado componentes o servicios particulares de un sistema para manejar una carga mayor. De esta manera, el sistema escalará bien para manejar la carga y también garantizará que las transacciones procesadas sean consistentes y estén en orden, ya que todos los servicios replicados obtendrán sus instrucciones de la cola de mensajes que actuará como la única fuente de verdad.

    Servicio de cola simple de Amazon – SQS

    Como la mayoría de las otras ofertas de Amazon Web Services, Simple Queue Service (SQS) es una solución de cola de mensajes distribuida y completamente administrada por Amazon, al igual que la computación sin servidor a través de Chalice .

    SQS nos permite enviar y recibir mensajes o instrucciones entre componentes de software, lo que nos permite implementar y escalar microservicios en nuestros sistemas sin la molestia de configurar y mantener un sistema de cola.

    Al igual que otros servicios de AWS, SQS se escala dinámicamente en función de la demanda y, al mismo tiempo, garantiza la seguridad de los datos transmitidos a través del cifrado (opcional) de los mensajes.

    Proyecto de demostración

    Para explorar Amazon Simple Queue Service, crearemos un sistema desacoplado en Node.js, en el que cada componente interactuará con los demás enviando y recuperando mensajes de SQS.

    Dado que somos una organización pequeña que no tiene el ancho de banda para manejar los pedidos a medida que llegan, tendremos un servicio para recibir los pedidos de los usuarios y otro que entregará todos los pedidos publicados ese día en nuestra bandeja de entrada de correo electrónico a una hora determinada del día para el procesamiento por lotes. Todos los pedidos se almacenarán en la cola hasta que sean recogidos por nuestro segundo servicio y enviados a nuestra bandeja de entrada de correo electrónico.

    Nuestros microservicios consistirán en API simples de Node.js, una que recibe la información del pedido de los usuarios y otra que envía correos electrónicos de confirmación a los usuarios.

    Enviar confirmaciones de correo electrónico de forma asincrónica a través de la cola de mensajería permitirá que nuestro servicio de pedidos continúe recibiendo pedidos a pesar de la carga ya que no tiene que preocuparse por enviar los correos electrónicos.

    Además, en caso de que el servicio de correo se caiga, una vez que se vuelva a activar, continuará enviando correos electrónicos desde la cola, por lo que no tendremos que preocuparnos por los pedidos perdidos.

    Servicios web de Amazon

    Para este proyecto, necesitaremos una cuenta de AWS activa y válida en la que podamos registrarnos en la página de inicio de AWS . AWS requiere que no solo ofrezcamos algunos datos personales, sino también nuestros datos de facturación. Para evitar que se le cobre por este proyecto de demostración, utilizaremos la capa gratuita de AWS con fines de prueba y desarrollo.

    También necesitaremos instalar la herramienta AWS CLI para interactuar con nuestros recursos de AWS desde nuestras máquinas. Las instrucciones para instalar la herramienta AWS CLI en varias plataformas se pueden encontrar aquí .

    Con la herramienta AWS CLI en su lugar, podemos ir a la Consola de AWS y debajo de nuestro menú desplegable de perfil hay una sección llamada «Mis credenciales de seguridad». Aquí podremos crear credenciales que se utilizarán al interactuar con la consola de AWS.

    Estas credenciales también serán utilizadas por la herramienta CLI de Amazon, que configuraremos ejecutando:

    $ aws configure
    

    Obtendremos un mensaje para llenar en nuestras Access Key ID, Secret Access Keyy las regiones predeterminadas y formatos de salida. Los dos últimos son opcionales pero necesitaremos la clave de acceso y el secreto que obtuvimos del tablero de la consola de AWS.

    Con nuestra cuenta de AWS en funcionamiento, y la AWS CLI configurada, podemos configurar nuestro AWS Simple Queue Service navegando a la página de inicio de SQS .

    Como podemos ver en la siguiente captura de pantalla, después de especificar nuestro nombre de cola, se nodeshop.fifonos presentan dos opciones de cola:

    Tenemos la opción de elegir entre una Cola Estándar o una Cola FIFO. Una cola estándar no mantiene el orden de los mensajes que recibe y es más adecuada para proyectos que priorizan el rendimiento sobre el orden de los eventos.

    Una cola FIFO, por otro lado, mantiene el orden de los mensajes tal como se reciben y también se recuperan en el mismo orden primero en entrar, primero en salir.

    Dado que estaremos construyendo una mini plataforma de compras, es importante mantener el orden de las solicitudes ya que esperamos vender artículos a las personas en el orden de sus compras. Una vez que hemos elegido el tipo de cola que requerimos, podemos modificar alguna configuración adicional de nuestra cola:

    Podemos configurar el tiempo antes de que un mensaje se elimine automáticamente de una cola y el tamaño de un mensaje, entre otras opciones. Por ahora, configuraremos nuestra cola FIFO usando los valores predeterminados. Con nuestra cola lista, ahora podemos crear nuestras API Node.js que leerán y escribirán en nuestra cola FIFO de Amazon SQS.

    Configuración de Node.js y NPM

    La instrucción para establecer Node.js en múltiples plataformas se puede encontrar aquí en la página web oficial de Node.js . Node Package Manager (NPM) se envía con Node.js y podemos verificar nuestra instalación de la siguiente manera:

    # Node.js version
    $ node -v
    v12.12.0
    
    # NPM version
    $ npm -v
    6.11.3
    

    El siguiente paso es configurar nuestras carpetas de la siguiente manera:

    # create folder and move into it
    $ mkdir nodeshop_apis && cd $_
    
    # create the orders and emails services folders
    $ mkdir orderssvc emailssvc
    

    Configuración de las API de node

    ordersPrimero crearemos el servicio ya que es el que recibe los pedidos de los usuarios y publica la información en nuestra cola. Nuestro emailsservicio luego leerá de la cola y enviará los correos electrónicos.

    Inicializaremos un proyecto Node.js e instalaremos el marco Express.js , que usaremos para construir nuestra API minimalista. También instalaremos el middleware body-parser para manejar nuestros datos de solicitud por nosotros y también validarlos.

    Para lograr esto en nuestra carpeta raíz:

    # initialize node project
    $ npm init
    
    # install express and body-parser
    $ npm install express body-parser --save
    

    Una vez body-parserinstalados Express y , se añadirán automáticamente a la sección de dependencias de nuestro package.jsonarchivo gracias a la --saveopción.

    Dado que tendremos múltiples servicios que se ejecutarán al mismo tiempo, también instalaremos el paquete npm-run-all para ayudarnos a iniciar todos nuestros servicios al mismo tiempo y no tener que ejecutar comandos en múltiples Windows de terminal:

    $ npm install npm-run-all --save
    

    Con npm-run-allinstalado, ahora modifiquemos la scriptsentrada en nuestro package.jsonarchivo para incluir los comandos para iniciar nuestros servicios y un comando para ejecutarlos todos:

    {
      // Truncated for brevity...
      "scripts": {
        "start-orders-svc": "node ./orderssvc/index.js 8081",
        "start-emails-svc": "node ./emailssvc/index.js",
        "start": "npm-run-all -p -r start-orders-svc"
      },
      // ...
    }
    

    Agregaremos los comandos start-orders-svcy start-emails-svcpara ejecutar nuestros servicios ordersy emailsrespectivamente. Luego configuraremos el startcomando para ejecutarlos usando npm-run-all.

    Con esta configuración, ejecutar todos nuestros servicios será tan fácil como ejecutar el siguiente comando:

    $ npm start
    

    Podemos crear nuestra ordersAPI en el index.jsarchivo de la siguiente manera:

    const express = require('express');
    const bodyParser = require('body-parser');
    
    const port = process.argv.slice(2)[0];
    const app = express();
    
    app.use(bodyParser.json());
    
    app.get('/index', (req, res) => {
        res.send("Welcome to NodeShop Orders.")
    });
    
    console.log(`Orders service listening on port ${port}`);
    app.listen(port);
    

    Después de agregar las bibliotecas requeridas a nuestro Express app, el punto final «/ index» responderá simplemente enviando un mensaje de bienvenida. Finalmente, la API escuchará en un puerto que especificaremos al iniciarlo.

    Iniciaremos la aplicación ejecutando el npm startcomando e interactuaremos con nuestras API utilizando la aplicación Postman :

    Implementaremos el emailsservicio más adelante. Por ahora, nuestro ordersservicio está configurado y ahora podemos implementar nuestra lógica comercial.

    Implementación: Servicio de pedidos

    Para implementar nuestra lógica comercial, comenzaremos con el ordersservicio que recibirá nuestros pedidos y los escribiremos en nuestra cola de Amazon SQS.

    Lo lograremos mediante la introducción de una nueva ruta y controlador para manejar la entrada de pedidos del usuario final y enviar los datos del pedido a nuestra cola de Amazon SQS.

    Antes de implementar el controlador, necesitaremos instalar el SDK de Amazon para Node.js:

    $ npm install aws-sdk --save
    

    Nuestro nuevo punto de enlace «/ order» recibirá una carga útil que contiene los datos del pedido y lo enviará a nuestra cola de SQS mediante el SDK de AWS:

    // ./orderssvc/index.js
    
    //
    // Code removed for brevity...
    //
    
    // Import the AWS SDK
    const AWS = require('aws-sdk');
    
    // Configure the region
    AWS.config.update({region: 'us-east-1'});
    
    // Create an SQS service object
    const sqs = new AWS.SQS({apiVersion: '2012-11-05'});
    const queueUrl = "SQS_QUEUE_URL";
    
    // the new endpoint
    app.post('/order', (req, res) => {
    
        let orderData = {
            'userEmail': req.body['userEmail'],
            'itemName': req.body['itemName'],
            'itemPrice': req.body['itemPrice'],
            'itemsQuantity': req.body['itemsQuantity']
        }
    
        let sqsOrderData = {
            MessageAttributes: {
              "userEmail": {
                DataType: "String",
                StringValue: orderData.userEmail
              },
              "itemName": {
                DataType: "String",
                StringValue: orderData.itemName
              },
              "itemPrice": {
                DataType: "Number",
                StringValue: orderData.itemPrice
              },
              "itemsQuantity": {
                DataType: "Number",
                StringValue: orderData.itemsQuantity
              }
            },
            MessageBody: JSON.stringify(orderData),
            MessageDeduplicationId: req.body['userEmail'],
            MessageGroupId: "UserOrders",
            QueueUrl: queueUrl
        };
    
        // Send the order data to the SQS queue
        let sendSqsMessage = sqs.sendMessage(sqsOrderData).promise();
    
        sendSqsMessage.then((data) => {
            console.log(`OrdersSvc | SUCCESS: ${data.MessageId}`);
            res.send("Thank you for your order. Check you inbox for the confirmation email.");
        }).catch((err) => {
            console.log(`OrdersSvc | ERROR: ${err}`);
    
            // Send email to emails API
            res.send("We ran into an error. Please try again.");
        });
    });
    

    El AWS SDK requiere que creemos un objeto de carga útil especificando los datos que estamos enviando a la cola, en nuestro caso lo definimos como sqsOrderData.

    Luego pasamos este objeto a la sendMessage()función que enviará nuestro mensaje a la cola usando las credenciales que usamos para configurar la AWS CLI. Finalmente, esperamos la respuesta y notificamos al usuario que su pedido ha sido recibido con éxito y que debe verificar el correo electrónico de confirmación.

    Para probar el ordersservicio, ejecutamos el comando npm starty enviamos la siguiente carga útil a localhost:8081/order:

    {
        "itemName": "Phone case",
        "itemPrice": "10",
        "userEmail": "[email protected]",
        "itemsQuantity": "2"
    }
    

    Esto enviará nuestro pedido al ordersservicio, desde donde se enviará el mensaje a nuestra cola de SQS. Podemos ver el pedido en la cola de SQS a través de la consola de AWS, como se muestra:

    Nuestro ordersservicio ha podido recibir el pedido de un usuario y enviar con éxito los datos a nuestra cola en el Servicio de Cola Simple.

    Implementación: Servicio de correos electrónicos

    Nuestro ordersservicio está listo y ya está recibiendo pedidos de los usuarios. El emailsservicio se encargará de leer los mensajes almacenados en la cola y enviar correos electrónicos de confirmación a los usuarios. Este servicio no es notificado cuando se realizan pedidos y, por lo tanto, debe seguir revisando la cola para ver si hay nuevos pedidos.

    Para asegurarnos de que nuestro emailsservicio comprueba continuamente si hay nuevos pedidos, utilizaremos la sqs-consumerbiblioteca que comprobará de forma continua y periódica si hay nuevos pedidos y enviará los correos electrónicos a los usuarios. sqs-consumertambién eliminará los mensajes de la cola una vez que los haya leído correctamente de la cola.

    Comenzaremos instalando la sqs-consumerbiblioteca ejecutando el siguiente comando:

    $ npm install sqs-consumer --save
    

    Ahora podemos implementar el emailsservicio de la siguiente manera:

    const AWS = require('aws-sdk');
    const { Consumer } = require('sqs-consumer');
    
    // Configure the region
    AWS.config.update({region: 'us-east-1'});
    
    const queueUrl = "SQS_QUEUE_URL";
    
    // Configure Nodemailer to user Gmail
    let transport = nodemailer.createTransport({
        host: 'smtp.googlemail.com',
        port: 587,
        auth: {
            user: 'Email address',
            pass: 'Password'
        }
    });
    
    function sendMail(message) {
        let sqsMessage = JSON.parse(message.Body);
        const emailMessage = {
            from: 'sender_email_adress',    // Sender address
            to: sqsMessage.userEmail,     // Recipient address
            subject: 'Order Received | NodeShop',    // Subject line
            html: `<p>Hi ${sqsMessage.userEmail}.</p. <p>Your order of ${sqsMessage.itemsQuantity} ${sqsMessage.itemName} has been received and is being processed.</p> <p> Thank you for shopping with us! </p>` // Plain text body
        };
    
        transport.sendMail(emailMessage, (err, info) => {
            if (err) {
                console.log(`EmailsSvc | ERROR: ${err}`)
            } else {
                console.log(`EmailsSvc | INFO: ${info}`);
            }
        });
    }
    
    // Create our consumer
    const app = Consumer.create({
        queueUrl: queueUrl,
        handleMessage: async (message) => {
            sendMail(message);
        },
        sqs: new AWS.SQS()
    });
    
    app.on('error', (err) => {
        console.error(err.message);
    });
    
    app.on('processing_error', (err) => {
        console.error(err.message);
    });
    
    console.log('Emails service is running');
    app.start();
    

    Crearemos una nueva sqs-consumeraplicación usando la Consumer.create()función y proporcionaremos la URL de consulta y la función para manejar los mensajes extraídos de la cola de SQS.

    En nuestro caso, la función sendMail()tomará el mensaje obtenido de la cola, extraerá los detalles del pedido del usuario y luego enviará un correo electrónico al usuario que usa Nodemailer. Consulte nuestro artículo sobre el envío de correos electrónicos en Node.js, si desea obtener más información.

    Nuestro emailsservicio ya está listo. Para integrarlo a nuestro script de ejecución, simplemente modificaremos la scriptsopción en nuestro package.json:

    {
      // Truncated for brevity...
      "scripts": {
        "start-orders-svc": "node ./orderssvc/index.js 8081",
        "start-emails-svc": "node ./emailssvc/index.js",
        // Update this line
        "start": "npm-run-all -p -r start-orders-svc start-emails-svc"
      },
      // ...
    }
    

    Cuando enviamos un nuevo pedido a través del ordersservicio, recibimos el siguiente correo electrónico en nuestra bandeja de entrada:

    Conclusión

    En esta publicación, usamos Node.js y Express para crear una API que estaba destinada a recibir los pedidos de los usuarios y publicar los detalles del pedido en nuestra cola de SQS en AWS. Luego, creamos otro servicio para buscar los mensajes publicados en la cola y enviar correos electrónicos de confirmación a los usuarios que publicaron los pedidos.

    Separamos la lógica de pedidos de la lógica de administración de correo electrónico y reunimos los dos servicios mediante un sistema de cola de mensajes. De esta manera, nuestro ordersservicio puede manejar la realización de pedidos mientras el emailsservicio envía los correos electrónicos a los usuarios.

    El código fuente de este proyecto está disponible aquí en GitHub .

    Etiquetas:

    Deja una respuesta

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