Vai al contenuto
Messaggistica

Messaggistica 101: queue vs stream vs pub/sub 📬

Se in team avete la frase “mettiamo Kafka” come risposta universale… questa pagina serve a ridurre i danni.

Il punto non è lo strumento: è il modello operativo 🧠

Quando scegli un sistema di messaggistica stai scegliendo:

  • come gestisci durabilità e replay;
  • che garanzie hai su ordering;
  • come fai scalare i consumer;
  • come gestisci errori e retry.

Queue (coda) 🧺

    flowchart LR
  P[Producer] --> Q[(Queue)]
  Q --> C[Consumer]
  C -->|ack| Q
  

Modello: messaggi consumati e “rimossi” (concettualmente) dopo l’ack.

  • Ottima per: task asincroni, job distribuiti, workload “competing consumers”.
  • Attenzioni: DLQ, visibility timeout, retry storm, poison messages.

Stream (log di eventi) 🧾

    flowchart LR
  P[Producer] --> S[(Stream / Log)]
  S --> CG1[Consumer group A]
  S --> CG2[Consumer group B]
  CG1 -->|offset| S
  CG2 -->|offset| S
  

Modello: append-only log con retention; i consumer mantengono un offset.

  • Ottima per: integrazioni multiple, audit/replay, data pipeline, proiezioni (CQRS).
  • Attenzioni: partitioning, reprocessing, schema evolution, consumer lag.

Pub/sub 📣

    flowchart LR
  P[Publisher] --> T[(Topic)]
  T --> S1[Subscriber 1]
  T --> S2[Subscriber 2]
  T --> S3[Subscriber 3]
  

Modello: publish e fan-out verso subscriber (spesso con filtri).

  • Ottima per: notifiche, integrazioni a molti consumer, trigger near real-time.
  • Attenzioni: filtri, rate limits, backpressure, governance dei topic.

Cose che confondono sempre (e non dovrebbero) 🧩

Ordering 🧵

Quando qualcuno dice “serve ordering”, la domanda vera è: ordering di cosa, dove, e con quale chiave? Molti broker lo garantiscono, ma con condizioni molto specifiche.

  • spesso è garantito solo per chiave/partizione;
  • se cambi la chiave di partizionamento a metà progetto… preparati a spiegare perché l’ordine “non è più un diritto costituzionale”.

Consumer group 🧑‍🤝‍🧑

È lo strumento base per scalare: più consumer, più throughput. Ma porta con sé concorrenza, race conditions e “perché l’evento è arrivato dopo?” (spoiler: non è arrivato dopo, lo hai processato dopo).

  • Stesso gruppo: i consumer si dividono il lavoro — ogni messaggio viene consegnato a uno solo (competing consumer). È il modello di scalabilità orizzontale per distribuire carico.
  • Gruppi diversi sullo stesso topic: ogni gruppo riceve tutti i messaggi in modo indipendente (fan-out). I consumer group A e B leggono entrambi l’intero stream senza interferirsi.

Riepilogo:

Scenario Comportamento
Consumer A1, A2 nello stesso gruppo Ogni messaggio va a uno solo (competing)
Consumer gruppo A + gruppo B sullo stesso topic Ogni gruppo riceve tutti i messaggi (fan-out)
  • scala orizzontalmente “dividendo” il lavoro;
  • richiede progettare bene le chiavi e accettare che più consumer = più concorrenza = più edge cases.

Retention e replay 🔁

Replay è potere (ricostruisci stato, rifai proiezioni, correggi bug), ma è anche un test di maturità: se non sei idempotente, è roulette.

  • se puoi fare replay, devi progettare idempotenza;
  • se non puoi fare replay, devi investire ancora di più su affidabilità e su come gestisci gli errori.

Criteri pratici per scegliere 🎛️

Chiediti:

  1. Ti serve replay per ricostruire stato/proiezioni? → stream.
  2. Ti serve distribuire job e non ti interessa la storia completa? → queue.
  3. Ti serve fan-out “pulito” a molti consumer? → pub/sub (o stream con più consumer group).
  4. Hai vincoli forti di ordering? → controlla come funziona davvero nel broker scelto.

Tre capacità spesso sottovalutate 🛠️

Nei confronti “broker A vs broker B”, spesso si guarda solo throughput e prezzo. Poi arrivano i problemi veri e scopri che mancavano tre domande fondamentali:

  • Filtering: il broker può filtrare eventi lato infrastruttura o stai scaricando tutto lato consumer? Se supporta filtri server-side (es. per header o attributi), risparmi bandwidth e CPU; altrimenti implementa filtri nel consumer e monitora gli scarti.
  • Backpressure/flow control: come rallenti producer o consumer quando il sistema satura? Preferisci meccanismi che fanno cadere la pressione verso il producer (rate limiting, pause) invece di retry incontrollati; a volte un buffer temporaneo con shed policy è più pratico.
  • Guaranteed delivery (pratica, non marketing): quali garanzie hai davvero su persistenza, ack, retry e duplicati? Verifica casi pratici (network partition, broker crash) e definisci cosa succede a livello applicativo: non accettare claim marketing senza test di recovery.

Queste tre cose non sono “nice to have”: spesso determinano costi operativi, latenza e serenità on-call.

Publish/subscribe, point-to-point, request/reply 📮

Queste tre etichette descrivono come viaggiano i messaggi — indipendentemente da quale broker usi.

  • Publish/subscribe: un evento viene consegnato a tutti i subscriber interessati (fan-out). Corrisponde al modello di più consumer group sullo stesso topic.
  • Point-to-point: un messaggio va a un singolo destinatario (competing consumer). Corrisponde al modello di più consumer nello stesso gruppo su una queue o partizione.
  • Request/reply: un producer aspetta una risposta da un recipient specifico. In EDA è utile quando serve una risposta deterministica, ma attenzione: se stai solo spostando sincronia sotto un tappeto di code, prima o poi inciampi.

Regola pratica: se stai ricreando request/response con una coda, molto probabilmente quello che cerchi è una chiamata HTTP/gRPC diretta.

Checklist di adozione (minima ma onesta) ✅

Questa non è burocrazia: è assicurazione. Senza queste decisioni esplicite, la piattaforma finisce in modalità “ognuno fa come gli pare” in circa due sprint.

  • Definisci naming per topic/stream.
  • Definisci ownership (chi è responsabile del canale).
  • Definisci policy: retention, DLQ, retry.
  • Definisci metriche: throughput, error rate, consumer lag.

Prossimi passi 🚀

Se ti è chiaro come si muovono i messaggi, il passo successivo è progettare cosa contengono e come li rendi operabili.

Ultimo aggiornamento il