Al llarg del mòdul hem tractat els esdeveniments com a unitats discretes: una comanda es crea, un pagament es confirma. Però hi ha una classe de problemes en què els esdeveniments arriben de forma contínua i a gran velocitat —clics d'usuaris, lectures de sensors IoT, transaccions de targeta, línies de log— i el valor està a processar-los a mesura que flueixen, sense esperar a acumular-los. Això és el streaming de dades en temps real: tractar les dades com un flux infinit (unbounded) sobre el qual apliquem transformacions, agregacions i detecció de patrons en qüestió de mil·lisegons.
El streaming és la base de casos d'ús com la detecció de frau a l'instant, els quadres de comandament en viu, les recomanacions en temps real o la monitorització de sistemes. En aquesta lliçó entendràs la diferència fonamental entre processament per lots (batch) i per fluxos (streaming), el paper de les finestres temporals, i com Kafka Streams ens permet implementar tot això amb codi relativament senzill.
Contingut
- Batch vs Streaming: la diferència fonamental
- Conceptes del processament de fluxos
- Finestres (windows): agrupar el temps
- Kafka Streams: processament sobre Kafka
- Exemple: detecció de frau per finestra
- Casos d'ús reals
- Errors comuns i consells
- Exercicis i solucions
- Conclusió
- Batch vs Streaming: la diferència fonamental
El processament per lots (batch) opera sobre un conjunt de dades finit i complet (per exemple, "totes les vendes d'ahir") en un moment programat. El processament per fluxos (streaming) opera sobre un flux infinit d'esdeveniments a mesura que arriben.
| Aspecte | Batch | Streaming |
|---|---|---|
| Dades | Finites, acotades | Infinites, contínues |
| Latència | Alta (minuts/hores) | Baixa (ms/segons) |
| Quan processa | En un moment programat | A mesura que arriben els esdeveniments |
| Visió de les dades | Completa | Parcial (el vist fins ara) |
| Exemple | Nòmina mensual, informe diari | Detecció de frau, alertes en viu |
| Eines típiques | Spark batch, ETL nocturn | Kafka Streams, Flink, Spark Streaming |
La frase clau: en batch tens totes les dades abans de començar; en streaming mai no les tens totes, perquè sempre en pot arribar una més. Això obliga a raonar d'una altra manera, especialment amb el temps.
- Conceptes del processament de fluxos
Abans de programar, fixem el vocabulari:
- Stream: seqüència infinita d'esdeveniments ordenats (aproximadament) en el temps.
- Transformacions sense estat (stateless): processen cada esdeveniment de forma independent. Exemples:
filter(descartar),map(transformar). - Transformacions amb estat (stateful): necessiten recordar informació d'esdeveniments anteriors. Exemples: comptar, sumar, agregar per clau. Requereixen un magatzem d'estat (state store).
- Temps de l'esdeveniment (event time) vs temps de processament (processing time):
- Event time: quan va passar l'esdeveniment (marca en el propi missatge).
- Processing time: quan el processa el sistema.
- Poden diferir molt (un esdeveniment d'un mòbil sense cobertura pot arribar minuts tard). El processament seriós es basa en event time.
- Dades tardanes (late data): esdeveniments que arriben després del que s'esperava. El sistema ha de decidir si els incorpora o els descarta.
- Finestres (windows): agrupar el temps
Com que el flux és infinit, no podem "sumar-ho tot". En el seu lloc, agrupem els esdeveniments en finestres temporals i calculem sobre cada finestra. Hi ha tres tipus principals:
flowchart TB
subgraph Tumbling["Tumbling (fixes, sense solapament)"]
direction LR
T1["[0-5min]"] --- T2["[5-10min]"] --- T3["[10-15min]"]
end
subgraph Hopping["Hopping/Sliding (solapades)"]
direction LR
H1["[0-5min]"] --- H2["[2-7min]"] --- H3["[4-9min]"]
end
subgraph Session["Session (per activitat)"]
direction LR
S1["activitat...gap...nova sessió"]
end| Tipus de finestra | Descripció | Cas d'ús |
|---|---|---|
| Tumbling (fixa) | Intervals fixos sense solapament. Cada esdeveniment cau en una sola finestra | "Vendes cada 5 minuts" |
| Hopping / Sliding (lliscant) | Intervals fixos que se solapen (avancen per salts) | "Mitjana mòbil dels últims 5 min, actualitzada cada minut" |
| Session (de sessió) | Es tanca després d'un període d'inactivitat (gap) | "Activitat d'un usuari fins que deixa d'interactuar" |
Exemple: per detectar frau busquem "més de 3 pagaments en 1 minut des de la mateixa targeta". Això és una agregació amb estat sobre una finestra d'1 minut agrupada per targeta.
- Kafka Streams: processament sobre Kafka
Kafka Streams és una llibreria Java que consumeix de topics de Kafka, processa les dades (filtratge, agregació, finestres, joins) i escriu els resultats en altres topics. No necessita un clúster a part: és codi que despleguescom una aplicació normal i que escala afegint instàncies.
Conceptes de la seva API:
- KStream: representa un flux d'esdeveniments (cada registre és un fet independent).
- KTable: representa una taula d'estat (cada clau té el seu últim valor; els nous registres actualitzen).
- State store: magatzem local (recolzat a Kafka) on es desen les agregacions amb estat, tolerant a fallades.
# Configuració mínima d'una app Kafka Streams application.id: detector-fraude # identifica l'app i els seus state stores bootstrap.servers: localhost:9092 # clúster de Kafka default.key.serde: ...StringSerde # com serialitzar les claus processing.guarantee: exactly_once_v2 # garantia de processament
application.idés crític: identifica l'aplicació i anomena els seus topics interns i state stores. Dues instàncies amb el mateixapplication.idformen un grup i es reparteixen la feina.processing.guarantee: exactly_once_v2activa el processament exactly-once dins de Kafka (entre topics), recolzant-se en transaccions.
- Exemple: detecció de frau per finestra
Detectem targetes amb més de 3 pagaments en una finestra d'1 minut:
StreamsBuilder builder = new StreamsBuilder();
// 1. Llegim el flux de pagaments del topic d'entrada
KStream<String, Pago> pagos = builder.stream("pagos");
pagos
// 2. Reagrupem per targeta (la clau passa a ser la targeta)
.groupBy((clave, pago) -> pago.tarjetaId(),
Grouped.with(Serdes.String(), pagoSerde))
// 3. Definim una finestra TUMBLING d'1 minut
.windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(1)))
// 4. Comptem els pagaments per targeta dins de cada finestra
.count(Materialized.as("conteo-pagos-por-tarjeta"))
// 5. Convertim la taula de resultats de nou en flux
.toStream()
// 6. Ens quedem només amb les que superen el llindar
.filter((tarjetaVentana, conteo) -> conteo > 3)
// 7. Emetem una alerta al topic de sortida
.map((tarjetaVentana, conteo) ->
KeyValue.pair(tarjetaVentana.key(),
new AlertaFraude(tarjetaVentana.key(), conteo)))
.to("alertas-fraude", Produced.with(Serdes.String(), alertaSerde));
KafkaStreams streams = new KafkaStreams(builder.build(), config);
streams.start();Explicació pas a pas:
builder.stream("pagos")crea unKStreamque llegeix cada pagament del topic d'entrada.groupByreagrupa pertarjetaId: ara els esdeveniments de la mateixa targeta comparteixen clau i es processen junts. És la base de qualsevol agregació amb estat.windowedBy(TimeWindows.ofSize... 1 minut)defineix finestres tumbling d'un minut.withNoGracesignifica que no esperem dades tardanes.count(...)manté, en un state store tolerant a fallades, quants pagaments porta cada targeta a la finestra actual.toStream()converteix laKTablede recomptes en un flux de canvis per poder continuar processant.filter(... conteo > 3)deixa passar només les combinacions targeta-finestra que superen el llindar de frau.mapconstrueix unaAlertaFraudeito("alertas-fraude")la publica al topic de sortida, on un altre servei podrà bloquejar la targeta o avisar el client.
El resultat és un detector de frau que reacciona en temps real, sense lots nocturns.
- Casos d'ús reals
- Detecció de frau: patrons anòmals de transaccions a l'instant (com l'exemple).
- Monitorització i alertes: detectar caigudes de servei o pics d'errors en els logs en viu.
- Quadres de comandament en temps real: vendes per minut, usuaris actius ara mateix.
- Recomanacions: ajustar suggeriments segons la navegació actual de l'usuari.
- IoT i telemetria: agregar lectures de milers de sensors i detectar llindars (temperatura, vibració).
- Enriquiment d'esdeveniments: unir (join) un flux de comandes amb una taula de clients per afegir dades al vol.
Errors Comuns i Consells
- Confondre event time amb processing time. Si agregues per processing time, un esdeveniment retardat cau en la finestra equivocada i els resultats són incorrectes. Basa les finestres en el temps de l'esdeveniment sempre que l'ordre importi.
- Ignorar les dades tardanes. Decideix explícitament la teva política: període de gràcia, descart o reprocessament. No decidir és decidir malament.
- Oblidar que l'estat creix. Les agregacions amb estat consumeixen memòria/disc. Fes servir finestres amb retenció acotada i neteja l'estat obsolet.
- Esperar "exactly-once" d'extrem a extrem. Kafka Streams ho garanteix entre topics de Kafka, però en escriure en sistemes externs tornes a necessitar idempotència (lliçó 05-02).
- Fer servir streaming quan n'hi havia prou amb un batch. Si la latència d'hores és acceptable, un batch és més simple i barat. El streaming afegeix complexitat operativa.
- Consell: comença amb transformacions sense estat (
filter,map); introdueix estat i finestres només quan el cas d'ús ho exigeixi.
Exercicis
- Per a cada cas, indica si faries servir batch o streaming i per què: (a) informe comptable de tancament mensual; (b) alerta quan un sensor supera 80 °C; (c) càlcul nocturn de comissions; (d) comptador d'usuaris connectats en directe.
- Vols calcular una "mitjana mòbil de temperatura dels últims 10 minuts, actualitzada cada 2 minuts". Quin tipus de finestra faries servir i per què?
- Explica amb les teves paraules la diferència entre
KStreamiKTablei posa un exemple de dada adequada per a cadascun.
Solucions
- (a) Batch: dades finites del mes, sense urgència. (b) Streaming: requereix reacció immediata. (c) Batch: procés programat nocturn sobre dades completes. (d) Streaming: mètrica en viu que canvia contínuament.
- Una finestra hopping/sliding (lliscant) de mida 10 minuts amb un salt (hop) de 2 minuts. És lliscant perquè les finestres se solapen: cada 2 minuts s'emet un nou resultat que cobreix els últims 10 minuts, que és justament el que demana una mitjana mòbil.
- Un
KStreammodela un flux de fets independents (cada registre és un esdeveniment que se suma als anteriors), per exemple cada pagament o cada clic. UnKTablemodela un estat per clau on cada nou registre actualitza el valor anterior d'aquella clau, per exemple el saldo actual de cada compte o l'últim preu de cada producte.
Conclusió
El streaming de dades tracta els esdeveniments com un flux infinit i els processa a mesura que arriben, aconseguint latències de mil·lisegons enfront de les hores del batch. Vam aprendre a raonar sobre el temps (event time vs processing time), a agrupar esdeveniments en finestres (tumbling, lliscants i de sessió) i a implementar agregacions amb estat en Kafka Streams mitjançant un detector de frau real. També vam veure quan convé streaming i quan n'hi ha prou amb un batch més senzill.
Amb aquesta lliçó tanquem el Mòdul 5: Arquitectures Dirigides per Esdeveniments i Missatgeria. Has recorregut el camí complet: des dels fonaments dels esdeveniments i la seva missatgeria asíncrona, passant per l'emmagatzematge amb Event Sourcing i CQRS, la coordinació de transaccions distribuïdes amb el patró Saga, fins al processament de fluxos en temps real. Aquests patrons constitueixen la columna vertebral dels sistemes distribuïts moderns i et preparen per dissenyar arquitectures escalables, resilients i reactives.
Curs d'Arquitectura d'Aplicacions
Mòdul 1: Fonaments de l'Arquitectura d'Aplicacions
- Què és l'Arquitectura d'Aplicacions?
- El Rol de l'Arquitecte de Programari
- Atributs de Qualitat i Requisits No Funcionals
- Decisions Arquitectòniques i Compromisos (Trade-offs)
- Documentació d'Arquitectura: Vistes i el Model C4
Mòdul 2: Principis i Tàctiques de Disseny
- Acoblament, Cohesió i Separació de Responsabilitats
- Principis SOLID Aplicats a l'Arquitectura
- DRY, KISS, YAGNI i Altres Principis de Disseny
- Tàctiques Arquitectòniques per als Atributs de Qualitat
- Gestió del Deute Tècnic
Mòdul 3: Estils i Patrons Arquitectònics
- Arquitectura Monolítica
- Arquitectura en Capes (N-Tier)
- Arquitectura Client-Servidor
- Arquitectura Hexagonal (Ports i Adaptadors)
- Arquitectura Neta i Ceba (Clean & Onion)
Mòdul 4: Arquitectures Distribuïdes i Microserveis
- Introducció als Sistemes Distribuïts
- Arquitectura de Microserveis
- Descomposició de Serveis i Bounded Contexts
- API Gateway, Service Discovery i Comunicació entre Serveis
- Patrons de Resiliència: Circuit Breaker, Retry i Bulkhead
- El Teorema CAP i la Consistència de Dades
Mòdul 5: Arquitectures Dirigides per Esdeveniments i Missatgeria
- Fonaments de l'Arquitectura Orientada a Esdeveniments
- Missatgeria Asíncrona: Cues i Brokers
- Patrons d'Esdeveniments: Event Sourcing i CQRS
- Gestió de Transaccions Distribuïdes: Patró Saga
- Streaming de Dades en Temps Real
Mòdul 6: Disseny Dirigit pel Domini (DDD)
- Conceptes Fonamentals del DDD
- Disseny Estratègic: Bounded Contexts i Llenguatge Ubic
- Disseny Tàctic: Entitats, Agregats i Repositoris
- Mapatge de Contextos (Context Mapping)
Mòdul 7: Dades i Persistència
- Estratègies de Persistència: SQL vs NoSQL
- Patrons d'Accés a Dades: Repository, Unit of Work i DAO
- Base de Dades per Servei i Gestió de Dades Distribuïdes
- Cau i Estratègies d'Invalidació
Mòdul 8: Arquitectura al Núvol i Desplegament
- Fonaments del Cloud Computing (IaaS, PaaS, SaaS)
- Contenidors i Orquestració amb Docker i Kubernetes
- Arquitectura Serverless
- Patrons de Disseny Cloud-Native
- Infraestructura com a Codi (IaC)
Mòdul 9: Qualitat, Seguretat i Observabilitat
- Escalabilitat: Horitzontal vs Vertical i Balanceig de Càrrega
- Alta Disponibilitat i Tolerància a Fallades
- Seguretat per Disseny i Autenticació/Autorització
- Observabilitat: Logging, Mètriques i Traçabilitat
- Rendiment i Proves de Càrrega
