Nella progettazione dei sistemi moderni, Event-Driven Architecture (EDA) si concentra sulla creazione, rilevazione, utilizzo e risposta agli eventi all’interno di un sistema. Gli eventi sono occorrenze significative che possono influenzare l’hardware o il software di un sistema, come le azioni degli utenti, i cambiamenti di stato o gli aggiornamenti dei dati.
L’EDA consente a diverse parti di un’applicazione di interagire in modo decoupled, permettendo loro di comunicare attraverso eventi anziché chiamate dirette. Questa configurazione consente ai componenti di lavorare in modo indipendente, rispondere agli eventi in modo asincrono e adattarsi alle esigenze aziendali in cambiamento senza una grande riconfigurazione del sistema, promuovendo l’agilità.
Le nuove e moderne applicazioni ora si basano fortemente sull’elaborazione dei dati in tempo reale e sulla reattività. L’importanza dell’EDA non può essere sottovalutata perché fornisce il framework che supporta tali requisiti. Utilizzando la comunicazione asincrona e le interazioni basate su eventi, i sistemi possono gestire in modo efficiente volumi elevati di transazioni e mantenere le prestazioni sotto carichi instabili. Queste caratteristiche sono particolarmente apprezzate in ambienti dove i cambiamenti sono molto spontanei, come le piattaforme di e-commerce o le applicazioni IoT.
Alcuni componenti chiave dell’EDA includono:
-
Sorgenti di Evento: Questi sono i produttori che generano eventi quando si verificano azioni significative all’interno del sistema. Esempi includono interazioni degli utenti o cambiamenti nei dati.
-
Ascoltatori: Questi sono entità che si iscrivono a eventi specifici e rispondono quando si verificano tali eventi. Gli ascoltatori consentono al sistema di reagire in modo dinamico ai cambiamenti.
-
Gestori: Questi sono responsabili dell’elaborazione degli eventi una volta che vengono rilevati dagli ascoltatori, eseguendo la logica aziendale necessaria o i flussi di lavoro attivati dall’evento.
In questo articolo, imparerai come implementare un’elaborazione dei dati basata su eventi utilizzando Traefik, Kafka e Docker.
Ecco una semplice applicazione ospitata su GitHub che puoi eseguire rapidamente per avere una panoramica di ciò che costruirai oggi.
Indice dei Contenuti
Ecco cosa tratteremo:
Cominciamo!
Prerequisiti
Prima di iniziare:
-
Implementare un’istanza di Ubuntu 24.04 con almeno 4 GB di RAM e un minimo di 20 GB di spazio libero su disco per ospitare immagini Docker, container e dati di Kafka.
-
Accedere all’istanza con un utente non root con privilegi sudo.
-
Aggiornare l’indice dei pacchetti.
sudo apt update
Comprensione delle Tecnologie
Apache Kafka
Apache Kafka è una piattaforma di streaming di eventi distribuita progettata per pipeline di dati ad alto throughput e applicazioni di streaming in tempo reale. Agisce come colonna vertebrale per l’implementazione di EDA gestendo efficientemente grandi volumi di eventi. Kafka utilizza un modello di pubblicazione-sottoscrizione in cui i produttori inviano eventi ai topic e i consumatori si iscrivono a questi topic per ricevere gli eventi.
Alcune delle principali caratteristiche di Kafka includono:
-
Alto Throughput: Kafka è in grado di gestire milioni di eventi al secondo con bassa latenza, rendendolo adatto per applicazioni ad alto volume.
-
Tolleranza ai guasti: L’architettura distribuita di Kafka garantisce la durata e la disponibilità dei dati anche di fronte a guasti dei server. Replica i dati su più broker all’interno di un cluster.
-
Scalabilità: Kafka può facilmente scalare in orizzontale aggiungendo più broker al cluster o partizioni ai topic, soddisfacendo le crescenti esigenze di dati senza significative riconfigurazioni.
Traefik
Traefik è un moderno reverse proxy HTTP e bilanciatore di carico progettato specificamente per architetture a microservizi. Scopre automaticamente i servizi in esecuzione nella tua infrastruttura e instrada il traffico di conseguenza. Traefik semplifica la gestione dei microservizi fornendo capacità di instradamento dinamico basate sui metadati del servizio.
Alcune delle caratteristiche chiave di Traefik includono:
-
Configurazione dinamica: Traefik aggiorna automaticamente la sua configurazione di instradamento con l’aggiunta o la rimozione dei servizi, eliminando l’intervento manuale.
-
Bilanciamento del Carico: distribuisce in modo efficiente le richieste in arrivo tra più istanze di servizio, migliorando le prestazioni e l’affidabilità.
-
Pannello di Controllo Integrato: Traefik fornisce un’interfaccia utente intuitiva per monitorare il traffico e la salute del servizio in tempo reale.
Utilizzando Kafka e Traefik in un’architettura basata su eventi, puoi costruire sistemi reattivi che gestiscono in modo efficiente l’elaborazione dei dati in tempo reale mantenendo alta disponibilità e scalabilità.
Come Impostare l’Ambiente
Come Installare Docker su Ubuntu 24.04
- Installa i pacchetti richiesti.
sudo apt install ca-certificates curl gnupg lsb-release
- Aggiungi la chiave GPG ufficiale di Docker.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
- Aggiungi il repository di Docker alle tue fonti 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
- Aggiorna di nuovo l’indice dei pacchetti e installa Docker Engine con il plugin Docker Compose.
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin
- Controlla per verificare l’installazione.
sudo docker run hello-world
Output Atteso:
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.
Come Configurare Docker Compose
Docker Compose semplifica la gestione delle applicazioni multi-contenitore, consentendo di definire e eseguire servizi in un unico file.
- Crea una directory di progetto
mkdir ~/kafka-traefik-setup && cd ~/kafka-traefik-setup
- Crea un file
docker-compose.yml
.
nano docker-compose.yml
- Aggiungi la seguente configurazione al file per definire i tuoi servizi.
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" # Traffico HTTP
- "8080:8080" # Dashboard di Traefik (non sicuro)
command:
- "--api.insecure=true"
- "--providers.docker=true"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
Salva le tue modifiche con ctrl + o
, poi esci con ctrl + x
.
- Avvia i tuoi servizi.
docker compose up -d
Output previsto:
[+] 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
Come costruire il sistema basato sugli eventi
Come creare produttori di eventi
Per produrre eventi in Kafka, dovrai implementare un produttore Kafka. Di seguito è riportato un esempio utilizzando Java.
- Crea un file
kafka-producer.java
.
nano kafka-producer.java
- Aggiungi la seguente configurazione per un Produttore 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) {
// Imposta le proprietà del produttore
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");
// Crea il produttore
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
try {
// Invia un messaggio al topic "my-topic"
ProducerRecord<String, String> record = new ProducerRecord<>("my-topic", "key1", "Hello, Kafka!");
RecordMetadata metadata = producer.send(record).get(); // Invio sincrono
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 {
// Chiudi il produttore
producer.close();
}
}
}
Salva le tue modifiche con ctrl + o
, poi esci con ctrl + x
.
Nella configurazione sopra, il produttore invia un messaggio con la chiave “key1” e il valore “Ciao, Kafka!” al topic “my-topic”.
Come configurare i topic di Kafka
Prima di produrre o consumare messaggi, è necessario creare dei topic in Kafka.
- Usa lo script
kafka-topics.sh
incluso nell’installazione di Kafka per creare un topic.
kafka-topics.sh --bootstrap-server localhost:9092 --create --topic <TopicName> --partitions <NumberOfPartitions> --replication-factor <ReplicationFactor>
Ad esempio, se desideri creare un topic chiamato my-topic
con 3 partizioni e un fattore di replica di 1, esegui:
docker exec <Kafka Container ID> /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic my-topic --partitions 3 --replication-factor 1
Output Atteso:
Created topic my-topic.
- Verifica se il Topic è stato creato con successo.
docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list
Output Atteso:
my-topic
Come Creare Consumatori di Eventi
Dopo aver creato i tuoi produttori e i topic, puoi creare consumatori per leggere i messaggi da quei topic.
- Crea un file
kafka-consumer.java
.
nano kafka-consumer.java
- Aggiungi la seguente configurazione per un consumatore 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) {
// Imposta le proprietà del consumatore
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");
// Crea il consumatore
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
// Iscriviti al topic
consumer.subscribe(Collections.singletonList("my-topic"));
try {
while (true) {
// Poll per nuovi record
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 {
// Chiudi il consumatore
consumer.close();
}
}
}
Salva le modifiche con ctrl + o
, quindi esci con ctrl + x
.
Nella configurazione sopra, il consumatore si iscrive a my-topic
e effettua il poll continuo per nuovi messaggi. Quando vengono ricevuti messaggi, stampa le loro chiavi e valori insieme alle informazioni sulla partizione e sull’offset.
Come Integrare Traefik con Kafka
Configura Traefik come Proxy Inverso.
Integrare Traefik come proxy inverso per Kafka ti consente di gestire il traffico in ingresso in modo efficiente, offrendo funzionalità come il routing dinamico e la terminazione SSL.
- Aggiorna il file
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" # Traffico HTTP
- "8080:8080" # Dashboard di Traefik (non sicura)
command:
- "--api.insecure=true"
- "--providers.docker=true"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
In questa configurazione, sostituisci kafka.example.com
con il tuo nome di dominio reale. Le etichette definiscono le regole di routing che Traefik utilizzerà per indirizzare il traffico al servizio Kafka.
- Riavvia i tuoi servizi.
docker compose up -d
-
Accedi alla dashboard di Traefik visitando
http://localhost:8080
nel tuo browser web.Bilanciamento del Carico con Traefik
Traefik offre capacità di bilanciamento del carico integrate che possono aiutare a distribuire le richieste su più istanze dei tuoi produttori e consumatori Kafka.
Strategie per il Bilanciamento del Carico nei Microservizi Basati su Eventi
- Round Robin:
Per impostazione predefinita, Traefik utilizza una strategia round-robin per distribuire equamente le richieste in arrivo su tutte le istanze disponibili di un servizio. Questo è efficace per bilanciare il carico quando sono in esecuzione più istanze di produttori o consumatori Kafka.
- Sessioni Sticky:
Se è necessario che le richieste da un cliente specifico vadano sempre alla stessa istanza (ad esempio, per mantenere lo stato della sessione), è possibile configurare sessioni sticky in Traefik utilizzando cookie o intestazioni.
- Controlli di Salute:
Configura i controlli di salute in Traefik per garantire che il traffico venga instradato solo verso istanze sane dei tuoi servizi Kafka. Puoi farlo aggiungendo parametri di controllo della salute nelle definizioni del servizio all’interno del tuo file 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"
Testare la Configurazione
Verifica della Produzione e Consumo degli Eventi
- Kafka fornisce strumenti da riga di comando incorporati per i test. Avvia un produttore della console.
docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-topic
Dopo aver eseguito questo comando, puoi digitare messaggi nel terminale, che verranno inviati al topic Kafka specificato.
- Avvia un’altra sessione del terminale e avvia un consumatore da console.
docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic my-topic --from-beginning
Questo comando visualizzerà tutti i messaggi in my-topic
, inclusi quelli prodotti prima che il consumatore iniziasse.
- Per vedere quanto bene i tuoi consumatori stanno tenendo il passo con i produttori, puoi eseguire il seguente comando per controllare il ritardo di un gruppo di consumatori specifico.
docker exec -it kafka-traefik-setup-kafka-1 /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group <your-consumer-group>
Monitoraggio e Registrazione
- Metrica Kafka:
Kafka espone numerose metriche che possono essere monitorate utilizzando JMX (Java Management Extensions). Puoi configurare JMX per esportare queste metriche ai sistemi di monitoraggio come Prometheus o Grafana. Le metriche chiave da monitorare includono:
-
Throughput dei Messaggi: Il tasso di messaggi prodotti e consumati.
-
Ritardo del Consumatore: La differenza tra l’offset dell’ultimo messaggio prodotto e l’offset dell’ultimo messaggio consumato.
-
Salute del Broker: Metriche relative alle prestazioni del broker, come tassi di richiesta e tassi di errore.
- Integrazione di Prometheus e Grafana:
Per visualizzare le metriche di Kafka, puoi configurare Prometheus per raccogliere metriche dai tuoi broker Kafka. Segui questi passaggi:
-
Abilita JMX Exporter sui tuoi broker Kafka aggiungendolo come agente Java nella configurazione del tuo broker.
-
Configura Prometheus aggiungendo un lavoro di scraping nel suo file di configurazione (
prometheus.yml
) che punta al tuo endpoint JMX Exporter. -
Usa Grafana per creare dashboard che visualizzano queste metriche in tempo reale.
Come implementare il monitoraggio per Traefik
- Endpoint delle metriche di Traefik.
Traefik fornisce supporto integrato per esportare metriche tramite Prometheus. Per abilitare questa funzionalità, aggiungi la seguente configurazione nella definizione del servizio Traefik all’interno di docker-compose.yml
:
command:
- "--metrics.prometheus=true"
- "--metrics.prometheus.addservice=true"
- Visualizzazione delle metriche di Traefik con Grafana.
Una volta che Prometheus inizia a raccogliere le metriche di Traefik, puoi visualizzarle utilizzando Grafana:
-
Crea una nuova dashboard in Grafana e aggiungi pannelli che mostrano le metriche chiave di Traefik come:
-
traefik_entrypoint_requests_total: Numero totale di richieste ricevute.
-
traefik_backend_request_duration_seconds: Tempi di risposta dei servizi di backend.
-
traefik_service_requests_total: Totale delle richieste inoltrate ai servizi di backend.
- Configurazione delle notifiche.
Configura le notifiche in Prometheus o Grafana in base a soglie specifiche (ad esempio, ritardi elevati dei consumatori o aumento dei tassi di errore).
Conclusione
In questa guida, hai implementato con successo l’Architettura basata sugli eventi (EDA) utilizzando Kafka e Traefik all’interno dell’ambiente Ubuntu 24.04.
Risorse aggiuntive
Per saperne di più puoi visitare:
Source:
https://www.freecodecamp.org/news/how-to-implement-event-driven-data-processing/