Recientemente, un desarrollador de una de las instituciones financieras me consultó sobre estrategias para administrar su puerta de enlace de SMS masivos que estaba teniendo demoras en la entrega a los clientes.
El mayor problema con esta escalabilidad de diseño, solo puede tener un trabajo en ejecución enviando los mensajes. Si por alguna codificación vudú lo logra a escala, tendrá que manejar su propia sincronización de procesos para que un solo mensaje no sea procesado por múltiples trabajos.
Como si eso no fuera suficiente, una vez que la sincronización funciona, el procesamiento de cada trabajo se ralentiza.
Índice
¿Por qué utilizar RabbitMq?
RabbitMq es un software de agente de mensajes de código abierto que implementa el Protocolo de cola de mensajes avanzado (AMQP) . Rabbitmq es liviano y fácil de implementar en las instalaciones y en la nube. Puedes leer más sobre rabbitmq aquí.
Ahora que sabe qué es RabbitMQ, la siguiente pregunta es: ¿por qué debería usar una cola en lugar de insertar en una tabla de base de datos y procesar con un trabajo? Hay un par de razones para esto:
- Mayor disponibilidad y mejor manejo de errores. Puede poner en cola un mensaje si el envío falla.
- Mejor escalabilidad: puede ejecutar varias instancias de un trabajo sin preocuparse por el procesamiento duplicado.
- Procesamiento de mensajes asincrónico.
El diagrama anterior muestra el diseño de nuestra puerta de enlace sms. En la arquitectura anterior, asumimos que una API web recibe todas las solicitudes y las reenvía a nuestra cola de mensajes (rabbitmq).
Dado que todo lo que hace esta API web es reenviar mensajes, tiene un alto rendimiento y puede manejar con elegancia tantos como sea posible con una sola instancia.
En el futuro, cuando esta api alcance su umbral, podemos ejecutar varias instancias en un enjambre de docker o usar un equilibrador de carga de software como pm2 para manejar las proverbiales 100 solicitudes por segundo.
El procesamiento de solicitudes (sms en cola) es manejado por trabajadores que consumen solicitudes de rabbitmq. Cada trabajador es un proceso de nodejs. Los procesos de nodejs se ejecutan en un clúster utilizando pm2.
Aunque estamos usando nodejs aquí, rabbitmq tiene bibliotecas cliente para java, c #, python, etc., incluso puede encontrar bibliotecas para rust. Si tiene la habilidad suficiente, puede escribir su propia biblioteca en su idioma favorito si no encuentra una. Puede escribir servicios de trabajador en cualquiera de estos lenguajes para consumir mensajes en rabbitmq.
RabbitMq se ejecuta en Erlang, por lo que debe instalar un tiempo de ejecución de Erlang antes de instalar RabbitMq. Puedes seguir esta para instalar rabbitMq localmente. Sin embargo, si tiene Docker, es un ninja y para nosotros, los ninjas, podemos instalar y configurar rabbitmq con un solo comando .
docker run -d -p 15672:15672 -p 5672:5672 --name brudexRabbit rabbitmq:3-management
Esto extraerá la imagen de rabbitmq si no está disponible y la ejecutará con los puertos predeterminados. Para aquellos de ustedes que todavía se preguntan brudexRabbit es el nombre que le estoy dando a este contenedor.
El siguiente código demuestra consumir el sms_messages
cola en rabbitmq. En el Worker.execute
método que llamamos el doWork
función que envía los mensajes sms llamando controller.sendSms
. Aunque no se implementó en este tutorial, controller.sendSms
a su vez, llamará a un proveedor de sms como Infobip, Twilio o Plivo para enviar el sms.
La arquitectura anterior envía sms a tres proveedores. En entornos de alta disponibilidad como bancos y otros sistemas financieros, es muy importante tener más de un proveedor de SMS para cambiar entre ellos cuando un proveedor de servicios deja de funcionar.
const Worker = ; const queue = 'sms_messages'; const amqp = require('amqplib/callback_api'); const controller = require('../controllers/smsmessage_controller');function doWork(rawData) if(rawData) try let json = JSON.parse(rawData); controller.sendSms(json); catch(e) console.log(`There was an error in queue $queue>> `,e); Worker.execute = function() amqp.connect(config.rabbitmq, function(error0, connection) if (error0) throw error0; connection.createChannel(function(error1, channel) if (error1) throw error1; channel.assertQueue(queue, durable: false ); channel.consume(queue, function(msg) console.log(" [x] Received %s", msg.content.toString()); doWork(msg.content.toString()); const now = Math.round(new Date().getTime() / 1000); stats.uptime = now - startupTime; , noAck: true ); ); ); ;
Escalar los procesos de trabajo
Hay varias formas de escalar los trabajadores. Puede ejecutar la aplicación en el contenedor de la ventana acoplable y ejecutar la ventana acoplable en modo enjambre o usar kubernetes.
En este tutorial, ejecutaremos los procesos de nodejs en pm2, un administrador de procesos de nodejs en modo cluster. Puede instalar pm2 globalmente con:
Ejecute PM2 en modo clúster con el siguiente comando:
pm2 start app.js -i max
Publicar / enviar mensajes a RabbitMq
El envío de mensajes a rabbitmq se denomina publicación. La arquitectura anterior asume que los mensajes se enviarán a la cola utilizando una API web escrita en cualquier idioma.
RabbitMq tiene sdks para todos los lenguajes populares, incluido rust, excepto el lenguaje de programación V, que nadie sabe quién lo está usando.
En esta publicación, demostramos cómo enviar mensajes usando C # asumiendo que es una aplicación netcore. Para aquellos de ustedes que todavía usan .Net framework, no sé cuán arcaicos pueden ser, pero cambien a netcore ahora.
public class SmsMessage { public string mobile { get; set; } public string sender { get; set; } public string message { get; set; } public string appId { get; set; } public string reference { get; set; } } public class SmsConsumer { public SmsConsumer() { _factory = new ConnectionFactory() HostName = "localhost" }; private static ConnectionFactory _factory; private static IConnection _connection; private const string QueueName = "sms_messages"; public void ProcessMessages() { using (_connection = _factory.CreateConnection()) { using (var channel = _connection.CreateModel()) { channel.QueueDeclare(queue: QueueName, durable: false, exclusive: false, autoDelete: false, arguments: null); //Formulate the sms message object var smsMessage = new SmsMessage appId = "DefaultApp", mobile = "233246583910", message = "Call Me Back", sender = "Brudex:Yours Truly", reference = "sms-123-987-098" ; //convert the object to json string string message = JsonConvert.SerializeObject(smsMessage); var body = Encoding.UTF8.GetBytes(message); // channel.BasicPublish(exchange: "", routingKey: "", basicProperties: null, body: body); } } } }
En el código anterior creamos una conexión a rabbitMq, creamos un canal con la conexión y usamos ese canal para publicar los mensajes en la cola. sms_messages
.
Hay varios conceptos de rabbitmq que no se exploraron en este tutorial, como intercambios y tipos de mensajes, por lo que nuestro exchange
y routingKey
las propiedades están vacías en el código anterior.
Puede publicar y consumir mensajes de rabbitmq para resolver la solución de SMS masivos para publicar abordajes sin conocer estos conceptos. Las colas de RabbitMq son idempotentes, lo que significa que las colas se crearán automáticamente si no están disponibles en el primer uso.
El tutorial anterior demostró cómo configurar un servicio de SMS masivo con rabbitmq como agente de mensajes. El código de este tutorial se encuentra aquí. Comparta sus comentarios, hágame saber cómo está manejando este tipo de problemas en su organización.
Gracias por leer este tutorial.
Añadir comentario