Cómo procesar miles de millones de eventos por segundo sin matar la latencia
Imagina esto: necesitas verificar transacciones bancarias en busca de fraude en tiempo real durante el proceso de pago. Solo tienes unos pocos milisegundos para tomar una decisión. Apache Kafka está transmitiendo eventos sin parar, pero para cada uno necesitas recuperar el historial del cliente de la base de datos. Si consultas un PostgreSQL o MySQL tradicional para cada mensaje, el sistema colapsará bajo la carga instantáneamente.
Este es un callejón sin salida común en el que los ingenieros se encuentran al diseñar sistemas de alta carga. Una caché regular como Redis ayuda a acelerar lecturas de claves individuales, pero cuando se trata de construir lógica de negocio compleja y análisis sobre un stream de datos, sus capacidades comienzan a ser insuficientes. Te ves obligado a unir cachés, brokers de mensajes y motores de procesamiento de terceros. En este punto, tiene sentido echar un vistazo a Hazelcast.
Hazelcast combina almacenamiento distribuido en memoria y un motor de procesamiento de streams en un solo sistema. En lugar de ensamblar una estructura a partir de tres servicios diferentes, obtienes una plataforma unificada capaz de ingestar, enriquecer y analizar datos sobre la marcha.
Qué está pasando dentro de la plataforma
El corazón de la plataforma es el motor Jet. Es responsable de construir pipelines de procesamiento de datos. Jet puede trabajar igualmente bien con streams continuos y conjuntos de datos estáticos, como buckets en Amazon S3 o tablas en una base de datos relacional.
Las métricas de rendimiento aquí son interesantes. Un solo nodo de Hazelcast puede agregar 10 millones de eventos por segundo manteniendo la latencia dentro de los 10 milisegundos. Si agrupas los servidores en cluster, el rendimiento escala hasta mil millones de eventos por segundo.
Para escribir consultas contra streams de datos, no necesitas sumergirte en APIs Java de bajo nivel. La plataforma soporta SQL estándar. Puedes escribir una SELECT familiar contra el stream de datos entrante, unirla con una tabla en memoria e inmediatamente enrutar el resultado al servicio de destino.
Así es como se ve la conexión de fuentes externas. De serie, obtienes un conjunto de conectores:
- Apache Kafka y JMS para trabajar con colas
- Hadoop y Amazon S3 para acceder a almacenamiento de archivos
- Bases de datos relacionales a través de JDBC estándar
- Modelos de Python para ejecutar machine learning directamente dentro del pipeline
Memoria distribuida y coordinación
Si eliminas el análisis de streams de la ecuación, Hazelcast sigue siendo un almacén clave-valor distribuido. Los datos se distribuyen a través de los nodos del cluster como particiones. Los desarrolladores tienen acceso a estructuras Java familiares (IMap, IQueue, ITopic), con la única diferencia de que están distribuidas a través de la red. Las búsquedas por clave toman microsegundos.
Para operaciones de base de datos, se soportan patrones clásicos de caché: read-through, write-through y write-behind. Al usar write-behind, la aplicación guarda datos exclusivamente en la RAM de Hazelcast, y la plataforma los vuelca de forma asíncrona al disco y a la base de datos principal. Si el DBMS relacional se cae temporalmente, tu aplicación continuará aceptando solicitudes sin fallos.
Una característica separada es la coordinación de microservicios. Hazelcast puede gestionar locks distribuidos, emitir secuencias de IDs únicos y mantener contadores compartidos. Esto elimina la necesidad de desplegar y mantener un cluster separado de Apache ZooKeeper para tareas mundanas de sincronización.
Cómo construir y ejecutar el proyecto
El código fuente del proyecto está escrito en Java. Se requiere JDK 17 o más reciente para compilar desde el código fuente. La forma más fácil de construir el proyecto es usar el script Maven Wrapper:
git pull origin master
./mvnw clean package -DskipTests
La compilación completa con todas las verificaciones puede tardar un rato. Si solo necesitas verificar rápidamente cambios locales, pasa el parámetro -Dquick:
./mvnw clean package -DskipTests -Dquick
Este parámetro desactiva la generación de Javadoc, las verificaciones de Checkstyle y las construcciones de módulos secundarios.
La situación de las pruebas es interesante. El repositorio tiene miles de pruebas divididas en tres perfiles:
- El perfil estándar
./mvnw testejecuta pruebas de integración rápidas. - El perfil nocturno
./mvnw test -P nightly-buildincluye pruebas lentas que no se pueden ejecutar en paralelo. - El perfil completo
./mvnw test -P all-testsejecuta secuencialmente absolutamente todas las verificaciones usando la red.
Algunas pruebas dependen de Docker. Si Docker no está instalado en tu máquina, esas pruebas fallarán. Para deshabilitarlas, usa el parámetro -Dhazelcast.disable.docker.tests. Al crear un Pull Request, el servidor CI del proyecto ejecuta el conjunto completo, así que localmente es suficiente con ejecutar las pruebas de tu propio módulo.
Puedes escribir clientes no solo en Java. La comunidad y la empresa mantienen librerías oficiales para Python, Node.js, .NET, C++ y Go.
Licencia y un par de matices prácticos
El código en el repositorio se divide en dos partes. El núcleo se distribuye bajo la permisiva Apache License 2.0. Sin embargo, algunas características y módulos empresariales están protegidos por la Hazelcast Community License. Esta prohíbe usar el código para crear servicios gestionados de pago (Cloud Service Provider) que compitan con el producto en la nube original de la empresa.
El segundo punto son los requisitos de recursos. Dado que todos los datos calientes residen en RAM, necesitarás adquirir una cantidad sustancial de memoria para trabajar con grandes volúmenes. Además, en un entorno Java, debes prestar mucha atención a la configuración del Garbage Collector para evitar pausas al limpiar gigabytes de memoria. Sin embargo, los ingenieros de Hazelcast mitigan este problema con almacenamiento off-heap, moviendo los datos fuera del heap de Java.
Quién debería echar un vistazo a Hazelcast
La plataforma funciona bien donde importa la respuesta a eventos en tiempo real:
- Prevención de fraude y scoring en fintech
- Procesamiento de telemetría y señales IoT de alta frecuencia
- Cálculo de precios y descuentos en e-commerce justo en el momento del clic del cliente
- Sincronización de datos entre centros de datos distribuidos (réplica WAN)
Si solo necesitas una caché simple para un par de endpoints, Hazelcast sería overkill: un Redis más simple manejaría esa tarea sin problemas. Pero si tu proyecto ha crecido hasta una escala donde el análisis de streams debe intersectarse con memoria distribuida sin viajes constantes al almacenamiento en disco, Hazelcast te ahorrará meses de desarrollo.
Proyectos relacionados