Table of Contents
Desafíos
Cuando trabajamos en una arquitectura de microservicios, nos enfrentamos al problema de la comunicación entre nuestras aplicaciones. A menudo, los servicios deben colaborar para gestionar una solicitud. Como las instancias de servicio suelen ser procesos que se ejecutan en varias máquinas o instancias virtuales, deben interactuar utilizando la comunicación entre procesos.
Los servicios pueden utilizar mecanismos de comunicación síncronos basados en peticiones/respuestas, como REST o gRPC, o pueden utilizar mecanismos de comunicación asíncronos basados en mensajes, como AMQP. En este artículo, nos centraremos en la mensajería asíncrona y abordaremos el problema de mantener la coherencia del orden de los mensajes y la coherencia eventual.
Consistencia eventual mediante la ordenación de mensajes
Preservar el orden de los mensajes puede ser un reto cuando se amplían los receptores de mensajes. Para procesar mensajes de forma simultánea, es habitual disponer de varias instancias de un servicio. El rendimiento de la aplicación aumenta cuando se utilizan varios hilos e instancias de servicio para procesar mensajes simultáneamente.
Los corredores de mensajes modernos, como Apache Kafka y AWS Kinesis, utilizan canales fragmentados (particionados) como solución común. Un canal fragmentado consta de dos o más fragmentos, cada uno de los cuales se comporta como un canal. El remitente especifica una clave de fragmento en la cabecera del mensaje. El agente de mensajería utiliza una clave de fragmento para asignar el mensaje a un fragmento/partición concreto. Por último, el agente de mensajería agrupa varias instancias de un receptor y las trata como el mismo receptor lógico. El agente de mensajería asigna cada fragmento a un único receptor.
Cada evento para un ID de mensaje concreto se publica en el mismo fragmento, que es leído por una única instancia de consumidor. Como resultado, se garantiza que estos mensajes se procesan en orden, asegurando la consistencia eventual.
Uso de los grupos de consumidores de Apache Kafka
Podemos procesar eventos en paralelo en Kafka para consumidores de un tema utilizando grupos de consumidores. A cada consumidor se le asigna un subconjunto de particiones de un tema y puede paralelizar el procesamiento de esos eventos. Podemos utilizar tres estrategias de partición primarias para los productores, Partición Round Robin, Partición de Clave de Mensaje y Partición Personalizada.
También nos centraremos en el Particionado de Clave de Mensaje, también conocido como Particionado por Defecto. Este método se utiliza cuando se proporciona una clave de mensaje. La clave se coloca a través de una función hash, y todos los mensajes con la misma clave se colocan en la misma partición, preservando el orden de los mensajes. Sin embargo, debemos ser cautos porque esta estrategia podría hacer que las particiones no se distribuyeran uniformemente y que las claves de mensajes más activas tuvieran particiones más grandes que las claves de mensajes menos activas. Al mismo tiempo, podemos asignar consumidores a grupos de consumidores específicos que tengan varias particiones.
Elegir la clave de fragmento correcta puede ser un reto; necesitamos distribuir los mensajes de manera uniforme y, si no elegimos la correcta, podríamos tener algunos consumidores con sobrecarga y otros con muy pocos mensajes. Por ejemplo, si en una aplicación de comercio electrónico establecemos el ID del producto como clave compartida, nos encontraremos con la desventajosa situación de que algunos consumidores estarán sobrecargados. Para solucionarlo, podríamos utilizar el ID de usuario como clave compartida, suponiendo que las compras se distribuyen equitativamente entre los usuarios.
Conclusión
En una arquitectura distribuida, la comunicación entre procesos desempeña un papel fundamental. Para aumentar la disponibilidad, utilizamos mensajería asíncrona. Una buena forma de diseñar una arquitectura basada en mensajes es utilizar un modelo de mensajes y canales. Para escalar instancias de consumidores y mantener el orden de los mensajes, podríamos utilizar canales fragmentados con una clave de mensaje como clave de fragmentación y asignar consumidores a fragmentaciones específicas, por ejemplo, grupos de consumidores en un sistema de mensajes Apache Kafka.
Si te ha gustado este artículo sobre la consistencia eventual a través del ordenamiento de mensajes, te sugiero que eches un ojo al blog de Apiumhub para leer más contenidos sobre arquitectura de software y consistencia eventual.