Introducción
Contenido
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:
- Amazon Simple Queue Service , que es el tema central de este artículo
- RabbitMQ , que es de código abierto y proporciona capacidades de mensajería asincrónica
- Apache Kafka , que es una plataforma de transmisión distribuida que admite el modo de interacción pub / sub
- Otros incluyen Apache RocketMQ , NSQ y HornetQ
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 Key
y 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.fifo
nos 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
orders
Primero crearemos el servicio ya que es el que recibe los pedidos de los usuarios y publica la información en nuestra cola. Nuestro emails
servicio 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-parser
instalados Express y , se añadirán automáticamente a la sección de dependencias de nuestro package.json
archivo gracias a la --save
opció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-all
instalado, ahora modifiquemos la scripts
entrada en nuestro package.json
archivo 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-svc
y start-emails-svc
para ejecutar nuestros servicios orders
y emails
respectivamente. Luego configuraremos el start
comando 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 orders
API en el index.js
archivo 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 start
comando e interactuaremos con nuestras API utilizando la aplicación Postman :
Implementaremos el emails
servicio más adelante. Por ahora, nuestro orders
servicio está configurado y ahora podemos implementar nuestra lógica comercial.
Implementación: Servicio de pedidos
Para implementar nuestra lógica comercial, comenzaremos con el orders
servicio 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 orders
servicio, ejecutamos el comando npm start
y enviamos la siguiente carga útil a localhost:8081/order
:
{
"itemName": "Phone case",
"itemPrice": "10",
"userEmail": "[email protected]",
"itemsQuantity": "2"
}
Esto enviará nuestro pedido al orders
servicio, 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 orders
servicio 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 orders
servicio está listo y ya está recibiendo pedidos de los usuarios. El emails
servicio 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 emails
servicio comprueba continuamente si hay nuevos pedidos, utilizaremos la sqs-consumer
biblioteca que comprobará de forma continua y periódica si hay nuevos pedidos y enviará los correos electrónicos a los usuarios. sqs-consumer
también eliminará los mensajes de la cola una vez que los haya leído correctamente de la cola.
Comenzaremos instalando la sqs-consumer
biblioteca ejecutando el siguiente comando:
$ npm install sqs-consumer --save
Ahora podemos implementar el emails
servicio 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-consumer
aplicació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 emails
servicio ya está listo. Para integrarlo a nuestro script de ejecución, simplemente modificaremos la scripts
opció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 orders
servicio, 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 orders
servicio puede manejar la realización de pedidos mientras el emails
servicio envía los correos electrónicos a los usuarios.
El código fuente de este proyecto está disponible aquí en GitHub .