En el diseño de sistemas modernos, la Arquitectura Orientada a Eventos (EDA, por sus siglas en inglés) se centra en crear, detectar, utilizar y responder a eventos dentro de un sistema. Los eventos son sucesos significativos que pueden afectar al hardware o software de un sistema, como acciones de usuario, cambios de estado o actualizaciones de datos.

EDA permite que diferentes partes de una aplicación interactúen de forma desacoplada, permitiéndoles comunicarse a través de eventos en lugar de llamadas directas. Esta configuración permite que los componentes trabajen de forma independiente, respondan a eventos de manera asíncrona y se adapten a las necesidades comerciales cambiantes sin necesidad de reconfiguración del sistema, promoviendo la agilidad.

Las nuevas y modernas aplicaciones ahora dependen en gran medida del procesamiento de datos en tiempo real y la capacidad de respuesta. La importancia de la EDA no puede ser subestimada porque proporciona el marco que respalda esos requisitos. Al utilizar comunicación asíncrona e interacciones orientadas a eventos, los sistemas pueden manejar eficientemente altos volúmenes de transacciones y mantener el rendimiento bajo cargas inestables. Estas características son especialmente apreciadas en entornos donde los cambios son muy espontáneos, como plataformas de comercio electrónico o aplicaciones de IoT.

Algunos componentes clave de la EDA incluyen:

  • Fuentes de Eventos: Estos son los productores que generan eventos cuando ocurren acciones significativas dentro del sistema. Ejemplos incluyen interacciones de usuario o cambios de datos.

  • Escuchas: Estas son entidades que se suscriben a eventos específicos y responden cuando esos eventos ocurren. Las escuchas permiten que el sistema reaccione dinámicamente a los cambios.

  • Manejadores: Estos son responsables de procesar los eventos una vez que son detectados por las escuchas, ejecutando la lógica de negocio necesaria o flujos de trabajo desencadenados por el evento.

En este artículo, aprenderás cómo implementar el procesamiento de datos basado en eventos utilizando Traefik, Kafka y Docker.

Aquí tienes una aplicación simple alojada en GitHub que puedes ejecutar rápidamente para tener una visión general de lo que construirás hoy.

Tabla de Contenidos

Aquí está lo que cubriremos:

¡Comencemos!

Requisitos previos

Antes de comenzar:

  • Despliega una instancia de Ubuntu 24.04 con al menos 4 GB de RAM y un mínimo de 20 GB de espacio libre en disco para alojar imágenes de Docker, contenedores y datos de Kafka.

  • Accede a la instancia con un usuario no root que tenga privilegios de sudo.

  • Actualiza el índice de paquetes.

sudo apt update

Comprendiendo las Tecnologías

Apache Kafka

Apache Kafka es una plataforma de transmisión de eventos distribuida, diseñada para pipelines de datos de alto rendimiento y aplicaciones de transmisión en tiempo real. Actúa como la columna vertebral para implementar EDA al gestionar eficazmente grandes volúmenes de eventos. Kafka utiliza un modelo de publicación-suscripción donde los productores envían eventos a temas, y los consumidores se suscriben a estos temas para recibir los eventos.

Algunas de las características clave de Kafka incluyen:

  • Alto Rendimiento: Kafka es capaz de manejar millones de eventos por segundo con baja latencia, lo que lo hace adecuado para aplicaciones de alto volumen.

  • Tolerancia a fallos: La arquitectura distribuida de Kafka garantiza la durabilidad y disponibilidad de datos incluso ante fallos de servidores. Replica datos en varios brokers dentro de un clúster.

  • Escalabilidad: Kafka puede escalar horizontalmente fácilmente agregando más brokers al clúster o particiones a los temas, satisfaciendo las crecientes necesidades de datos sin una reconfiguración significativa.

Traefik

Traefik es un moderno proxy inverso HTTP y balanceador de carga diseñado específicamente para arquitecturas de microservicios. Descubre automáticamente los servicios que se ejecutan en tu infraestructura y dirige el tráfico en consecuencia. Traefik simplifica la gestión de microservicios al proporcionar capacidades de enrutamiento dinámico basadas en metadatos del servicio.

Algunas de las características clave de Traefik incluyen:

  • Configuración dinámica: Traefik actualiza automáticamente su configuración de enrutamiento a medida que se agregan o eliminan servicios, eliminando la intervención manual.

  • Balanceo de Carga: Distribuye de manera eficiente las solicitudes entrantes entre múltiples instancias de servicio, mejorando el rendimiento y la fiabilidad.

  • Tablero Integrado: Traefik proporciona un tablero fácil de usar para monitorear el tráfico y la salud del servicio en tiempo real.

Al utilizar Kafka y Traefik en una arquitectura impulsada por eventos, puedes construir sistemas receptivos que manejan de manera eficiente el procesamiento de datos en tiempo real mientras mantienen alta disponibilidad y escalabilidad.

Cómo Configurar el Entorno

Cómo Instalar Docker en Ubuntu 24.04

  1. Instala los paquetes requeridos.
sudo apt install ca-certificates curl gnupg lsb-release
  1. Agrega la clave GPG oficial de Docker.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  1. Agrega el repositorio de Docker a tus fuentes de APT.
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  1. Actualiza el índice de paquetes nuevamente e instala Docker Engine con el complemento Docker Compose.
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  1. Verifica la instalación.
sudo docker run hello-world

Salida Esperada:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:305243c734571da2d100c8c8b3c3167a098cab6049c9a5b066b6021a60fcb966
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Cómo Configurar Docker Compose

Docker Compose simplifica la gestión de aplicaciones de múltiples contenedores, permitiéndote definir y ejecutar servicios en un solo archivo.

  1. Crea un directorio de proyecto
mkdir ~/kafka-traefik-setup && cd ~/kafka-traefik-setup
  1. Crea un archivo docker-compose.yml.
nano docker-compose.yml
  1. Agrega la siguiente configuración al archivo para definir tus servicios.
version: '3.8'

services:
  kafka:
    image: wurstmeister/kafka:latest
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9092,OUTSIDE://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
      KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9092
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181

  zookeeper:
    image: wurstmeister/zookeeper:latest
    ports:
      - "2181:2181"

  traefik:
    image: traefik:v2.9
    ports:
      - "80:80"       # Tráfico HTTP
      - "8080:8080"   # Panel de control de Traefik (no seguro)
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

Guarda tus cambios con ctrl + o, luego sal con ctrl + x.

  1. Inicia tus servicios.
docker compose up -d

Salida esperada:

[+] Running 4/4
 ✔ Network kafka-traefik-setup_default        Created                  0.2s
 ✔ Container kafka-traefik-setup-zookeeper-1  Started                  1.9s
 ✔ Container kafka-traefik-setup-traefik-1    Started                  1.9s
 ✔ Container kafka-traefik-setup-kafka-1      Started                  1.9s

Cómo construir el sistema de eventos impulsado

Cómo crear productores de eventos

Para producir eventos en Kafka, necesitarás implementar un productor de Kafka. A continuación se muestra un ejemplo usando Java.

  1. Crea un archivo kafka-productor.java.
nano kafka-producer.java
  1. Agrega la siguiente configuración para un Productor de Kafka.
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;

import java.util.Properties;

public class SimpleProducer {
    public static void main(String[] args) {
        // Configurar las propiedades del productor
        Properties props = new Properties();
        props.put("bootstrap.servers", "localhost:9092");
        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        // Crear el productor
        KafkaProducer<String, String> producer = new KafkaProducer<>(props);

        try {
            // Enviar un mensaje al tema "mi-tema"
            ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", "key1", "Hello, Kafka!");
            RecordMetadata metadata = producer.send(record).get(); // Envío síncrono
            System.out.printf("Sent message with key %s to partition %d with offset %d%n", 
                              record.key(), metadata.partition(), metadata.offset());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // Cerrar el productor
            producer.close();
        }
    }
}

Guarda tus cambios con ctrl + o, luego sal con ctrl + x.

En la configuración anterior, el productor envía un mensaje con la clave “key1” y el valor “¡Hola, Kafka!” al tema “mi-tema”.

Cómo configurar temas de Kafka

Antes de producir o consumir mensajes, necesitas crear temas en Kafka.

  1. Utilice el script kafka-topics.sh incluido en su instalación de Kafka para crear un tema.
kafka-topics.sh --bootstrap-server localhost:9092 --create --topic <TopicName> --partitions <NumberOfPartitions> --replication-factor <ReplicationFactor>

Por ejemplo, si desea crear un tema llamado my-topic con 3 particiones y un factor de replicación de 1, ejecute:

docker exec <Kafka Container ID> /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic my-topic --partitions 3 --replication-factor 1

Salida Esperada:

Created topic my-topic.
  1. Verifique para confirmar si el tema se creó con éxito.
docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list

Salida Esperada:

my-topic

Cómo Crear Consumidores de Eventos

Después de haber creado sus productores y temas, puede crear consumidores para leer mensajes de esos temas.

  1. Cree un archivo kafka-consumer.java.
nano kafka-consumer.java
  1. Agregue la siguiente configuración para un consumidor de Kafka.
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;

import java.time.Duration;
import java.util.Collections;
import java.util.Properties;

public class SimpleConsumer {
    public static void main(String[] args) {
        // Configurar las propiedades del consumidor
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "my-group");
        props.put(ConsumerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
        props.put(ConsumerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

        // Crear el consumidor
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

        // Suscribirse al tema
        consumer.subscribe(Collections.singletonList("my-topic"));

        try {
            while (true) {
                // Consultar nuevos registros
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
                for (ConsumerRecord<String, String> record : records) {
                    System.out.printf("Consumed message with key %s and value %s from partition %d at offset %d%n",
                                      record.key(), record.value(), record.partition(), record.offset());
                }
            }
        } finally {
            // Cerrar el consumidor
            consumer.close();
        }
    }
}

Guarde los cambios con ctrl + o, luego salga con ctrl + x.

En la configuración anterior, el consumidor se suscribe a my-topic y consulta continuamente nuevos mensajes. Cuando se reciben mensajes, imprime sus claves y valores junto con la información de partición y desplazamiento.

Cómo Integrar Traefik con Kafka

Configura Traefik como un Proxy Inverso.

Integrar Traefik como un proxy inverso para Kafka te permite gestionar el tráfico entrante de manera eficiente mientras proporciona características como el enrutamiento dinámico y la terminación SSL.

  1. Actualiza el archivo docker-compose.yml.
version: '3.8'

services:
  kafka:
    image: wurstmeister/kafka:latest
    ports:
      - "9092:9092"
    environment:
      KAFKA_ADVERTISED_LISTENERS: INSIDE://kafka:9092,OUTSIDE://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
      KAFKA_LISTENERS: INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9092
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.kafka.rule=Host(`kafka.example.com`)"
      - "traefik.http.services.kafka.loadbalancer.server.port=9092"

  zookeeper:
    image: wurstmeister/zookeeper:latest
    ports:
      - "2181:2181"

  traefik:
    image: traefik:v2.9
    ports:
      - "80:80"        # Tráfico HTTP
      - "8080:8080"    # Dashboard de Traefik (inseguro)
    command:
      - "--api.insecure=true"
      - "--providers.docker=true"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

En esta configuración, reemplaza kafka.example.com con tu nombre de dominio real. Las etiquetas definen las reglas de enrutamiento que Traefik utilizará para dirigir el tráfico al servicio Kafka.

  1. Reinicia tus servicios.
docker compose up -d
  1. Accede a tu dashboard de Traefik ingresando a http://localhost:8080 en tu navegador web.

    Balanceo de Carga con Traefik

    Traefik proporciona capacidades de balanceo de carga integradas que pueden ayudar a distribuir solicitudes entre múltiples instancias de tus productores y consumidores de Kafka.

    Estrategias para el Balanceo de Carga en Microservicios Basados en Eventos

    1. Round Robin:

Por defecto, Traefik utiliza una estrategia de round-robin para distribuir las solicitudes entrantes de manera uniforme entre todas las instancias disponibles de un servicio. Esto es efectivo para equilibrar la carga cuando se están ejecutando múltiples instancias de productores o consumidores de Kafka.

  1. Sesiones Pegajosas:

Si necesitas que las solicitudes de un cliente específico siempre vayan a la misma instancia (por ejemplo, para mantener el estado de la sesión), puedes configurar sesiones pegajosas en Traefik utilizando cookies o encabezados.

  1. Verificaciones de Salud:

Configura verificaciones de salud en Traefik para asegurarte de que el tráfico solo se dirija a instancias saludables de tus servicios de Kafka. Puedes hacer esto agregando parámetros de verificación de salud en las definiciones de servicio dentro de tu archivo docker-compose.yml:

    labels:
      - "traefik.http.services.kafka.loadbalancer.healthcheck.path=/health"
      - "traefik.http.services.kafka.loadbalancer.healthcheck.interval=10s"
      - "traefik.http.services.kafka.loadbalancer.healthcheck.timeout=3s"

Probando la Configuración

Verificando la Producción y Consumo de Eventos

  1. Kafka proporciona herramientas de línea de comandos integradas para pruebas. Inicia un productor de consola.
    docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-topic

Después de ejecutar este comando, puedes escribir mensajes en la terminal, que serán enviados al tema de Kafka especificado.

  1. Inicie otra sesión de terminal y comience un consumidor de consola.
    docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic my-topic --from-beginning

Este comando mostrará todos los mensajes en my-topic, incluidos aquellos producidos antes de que el consumidor comenzara.

  1. Para ver qué tan bien sus consumidores están manteniéndose al día con los productores, puede ejecutar el siguiente comando para verificar el retraso de un grupo de consumidores específico.
    docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group <your-consumer-group>

Monitoreo y Registro

  1. Métricas de Kafka:

Kafka expone numerosas métricas que se pueden monitorear utilizando JMX (Java Management Extensions). Puede configurar JMX para exportar estas métricas a sistemas de monitoreo como Prometheus o Grafana. Las métricas clave a monitorear incluyen:

  • Rendimiento de Mensajes: La tasa de mensajes producidos y consumidos.

  • Retraso del Consumidor: La diferencia entre el desplazamiento del último mensaje producido y el desplazamiento del último mensaje consumido.

  • Salud del Broker: Métricas relacionadas con el rendimiento del broker, como tasas de solicitud y tasas de error.

  1. Integración de Prometheus y Grafana:

Para visualizar las métricas de Kafka, puede configurar Prometheus para recoger métricas de sus brokers de Kafka. Siga estos pasos:

  • Habilita el Exportador JMX en tus brokers de Kafka añadiéndolo como un agente de Java en la configuración de tu broker.

  • Configura Prometheus añadiendo un trabajo de scrapeo en su archivo de configuración (prometheus.yml) que apunte a tu punto final del Exportador JMX.

  • Usa Grafana para crear paneles que visualicen estas métricas en tiempo real.

Cómo implementar la supervisión para Traefik

  1. Punto final de métricas de Traefik.

Traefik proporciona soporte incorporado para exportar métricas a través de Prometheus. Para habilitar esta función, añade la siguiente configuración en la definición de servicio de Traefik dentro de docker-compose.yml:

    command:
      - "--metrics.prometheus=true"
      - "--metrics.prometheus.addservice=true"
  1. Visualizando métricas de Traefik con Grafana.

Una vez que Prometheus esté scrapeando las métricas de Traefik, puedes visualizarlas usando Grafana:

  • Crea un nuevo panel en Grafana y añade paneles que muestren métricas clave de Traefik como:

  • traefik_entrypoint_requests_total: Número total de solicitudes recibidas.

  • traefik_backend_request_duration_seconds: Tiempos de respuesta de los servicios de backend.

  • traefik_service_requests_total: Total de solicitudes reenviadas a los servicios de backend.

  1. Configuración de Alertas.

Configura alertas en Prometheus o Grafana basadas en umbrales específicos (por ejemplo, alto retraso del consumidor o aumento de tasas de error).

Conclusión

En esta guía, implementaste con éxito la Arquitectura Orientada a Eventos (EDA) utilizando Kafka y Traefik dentro del entorno de Ubuntu 24.04.

Recursos Adicionales

Para aprender más, puedes visitar: