Stile di architettura guidato dagli eventi
Un'architettura basata su eventi è costituita da producer di eventi che generano un flusso di eventi, consumer di eventi in ascolto di questi eventi e canali di eventi che trasferiscono eventi dai producer ai consumer.
Gli eventi vengono recapitati praticamente in tempo reale, in modo che i consumer possano rispondervi immediatamente non appena si verificano. I produttori sono separati dai consumer: un produttore non sa quali consumer sono in ascolto. Anche i consumer sono separati tra loro e ognuno visualizza tutti gli eventi. Questo processo è diverso da un modello Consumer concorrenti in cui i consumer eseguono il pull dei messaggi da una coda e un messaggio viene elaborato una sola volta, presupponendo che non siano presenti errori. In alcuni sistemi, ad esempio Internet delle cose (IoT), gli eventi devono essere inseriti in volumi elevati.
Un'architettura basata su eventi può usare un modello di pubblicazione-sottoscrizione o un modello di flusso di eventi.
Pub/sub: L'infrastruttura di messaggistica publish-subscribe tiene traccia delle sottoscrizioni. Quando viene pubblicato un evento, il modello lo invia a ogni sottoscrittore. Non è possibile riprodurre un evento dopo la ricezione e i nuovi sottoscrittori non visualizzano l'evento.
Streaming di eventi: Gli eventi vengono scritti in un log. Gli eventi vengono ordinati rigorosamente all'interno di una partizione e sono durevoli. I client non sottoscrivono il flusso. Un client può invece leggere da qualsiasi parte del flusso. Il client è responsabile dell'avanzamento della posizione nel flusso. Ciò significa che un client può partecipare in qualsiasi momento e può riprodurre eventi.
Sul lato consumer si applicano alcune variazioni comuni:
Elaborazione semplice di eventi: Un evento attiva immediatamente un'azione nel consumer. Ad esempio, è possibile usare Funzioni di Azure con un trigger del bus di servizio di Azure in modo che una funzione venga eseguita ogni volta che un messaggio viene pubblicato in un argomento del bus di servizio.
Correlazione di eventi di base: Un consumer elabora alcuni eventi aziendali discreti, li correla con un identificatore e rende persistenti le informazioni degli eventi precedenti da usare durante l'elaborazione di eventi successivi. Le librerie come NServiceBus e MassTransit supportano questo modello.
Elaborazione di eventi complessi: Un consumer usa una tecnologia come Analisi di flusso di Azure per analizzare una serie di eventi e identificare i modelli nei dati degli eventi. Ad esempio, è possibile aggregare le letture da un dispositivo incorporato in un intervallo di tempo e generare una notifica se la media mobile supera una determinata soglia.
Elaborazione del flusso di eventi: Usare una piattaforma di streaming dei dati, ad esempio l'hub IoT di Azure o Apache Kafka, come pipeline per inserire gli eventi e inviarli ai processori di flusso. Gli elaboratori di flussi intervengono per elaborare o trasformare il flusso. Potrebbero essere presenti più processori di flusso per sottosistemi diversi dell'applicazione. Questo approccio è ideale per i carichi di lavoro IoT.
L'origine degli eventi potrebbe essere esterna al sistema, ad esempio i dispositivi fisici in una soluzione IoT. In tal caso, il sistema deve essere in grado di inserire i dati in corrispondenza del volume e della velocità effettiva richiesti dall'origine dati.
Esistono due approcci principali per strutturare i payload degli eventi. Quando si ha il controllo sui consumer di eventi, è possibile decidere la struttura del payload per ogni consumer. Questa strategia consente di combinare approcci in base alle esigenze all'interno di un singolo carico di lavoro.
Includere tutti gli attributi obbligatori nel payload: Usare questo approccio quando si desidera che i consumer dispongano di tutte le informazioni disponibili senza dover eseguire query su un'origine dati esterna. Tuttavia, può causare problemi di coerenza dei dati a causa di più sistemi di record, in particolare dopo gli aggiornamenti. La gestione dei contratti e il controllo delle versioni possono anche diventare complessi.
Includere solo le chiavi nel payload: In questo approccio, i consumer recuperano gli attributi necessari, ad esempio una chiave primaria, per recuperare in modo indipendente i dati rimanenti da un'origine dati. Questo metodo offre una migliore coerenza dei dati perché ha un singolo sistema di record. Tuttavia, può comportare prestazioni inferiori rispetto al primo approccio perché i consumer devono eseguire spesso query sull'origine dati. Per quanto riguarda l'accoppiamento, la larghezza di banda, la gestione dei contratti o il controllo delle versioni, si hanno meno preoccupazioni perché gli eventi più piccoli e i contratti più semplici riducono la complessità.
Nel diagramma precedente ogni tipo di consumer viene visualizzato come una singola casella. Per evitare che il consumer diventi un singolo punto di guasto nel sistema, è tipico avere più istanze di un consumer. Potrebbero essere necessarie più istanze anche per gestire il volume e la frequenza degli eventi. Un singolo consumer può elaborare eventi su più thread. Questa configurazione può creare problemi se gli eventi devono essere elaborati in ordine o richiedono una semantica di una sola volta. Per altre informazioni, vedere Ridurre al minimo il coordinamento.
Esistono due topologie primarie all'interno di molte architetture guidate dagli eventi:
Topologia broker: I componenti trasmettono le occorrenze come eventi all'intero sistema. Altri componenti agiscono sull'evento o ignorano l'evento. Questa topologia è utile quando il flusso di elaborazione degli eventi è relativamente semplice. Non esiste un coordinamento centrale o un'orchestrazione, quindi questa topologia può essere dinamica. Questa topologia è altamente disaccoppiata, il che consente di garantire scalabilità, velocità di risposta e tolleranza ai guasti dei componenti. Nessun componente è proprietario o è a conoscenza dello stato di qualsiasi transazione aziendale a più passaggi e le azioni vengono eseguite in modo asincrono. Successivamente, le transazioni distribuite sono rischiose perché non è possibile riavviare o riprodurre in modo nativo. È necessario considerare attentamente la gestione degli errori e le strategie di intervento manuale perché questa topologia può essere un'origine di incoerenza dei dati.
Topologia mediator: Questa topologia risolve alcune delle carenze della topologia broker. Esiste un mediator di eventi che gestisce e controlla il flusso degli eventi. Il mediatore di eventi mantiene lo stato e gestisce le funzionalità di gestione e riavvio degli errori. A differenza della topologia broker, in questa topologia, i componenti trasmettono le occorrenze come comandi e solo ai canali designati. Questi canali sono in genere code di messaggi. È previsto che i consumer eselaborino questi comandi. Questa topologia offre un maggiore controllo, una migliore gestione degli errori distribuiti e una coerenza dei dati potenzialmente migliore. Questa topologia introduce un maggiore accoppiamento tra i componenti e il mediatore eventi può diventare un collo di bottiglia o un problema di affidabilità.
Quando usare questa architettura
È consigliabile usare questa architettura quando:
- Più sottosistemi devono elaborare gli stessi eventi.
- È necessaria l'elaborazione in tempo reale con ritardo di tempo minimo.
- È necessaria un'elaborazione complessa degli eventi, ad esempio criteri di ricerca o aggregazione nel tempo.
- È necessario un volume elevato e una velocità elevata dei dati, ad esempio IoT.
Vantaggi
I vantaggi di questa architettura sono i seguenti:
- I producer e i consumer sono separati.
- Nessuna integrazione da punto a punto. È facile aggiungere nuovi consumer al sistema.
- I consumer possono rispondere immediatamente agli eventi quando si verificano.
- Scalabilità elevata, elastica e distribuita.
- I sottosistemi hanno viste indipendenti del flusso di eventi.
Problematiche
Recapito garantito.
In alcuni sistemi, in particolare in scenari IoT, è essenziale garantire che gli eventi vengano recapitati.
Elaborazione degli eventi in ordine o esattamente una volta.
Per resilienza e scalabilità, ogni tipo di consumer viene in genere eseguito in più istanze. Questo processo può creare una richiesta di verifica se gli eventi devono essere elaborati in ordine all'interno di un tipo di consumer o se la logica di elaborazione dei messaggi idempotenti non è implementata.
Coordinamento dei messaggi tra i servizi.
I processi aziendali hanno spesso più servizi che pubblicano e sottoscrivono i messaggi per ottenere un risultato coerente in un intero carico di lavoro. È possibile usare modelli di flusso di lavoro come il modello Coreografia e Saga Orchestration per gestire in modo affidabile i flussi di messaggi tra vari servizi.
Gestione degli errori.
L'architettura basata su eventi usa principalmente la comunicazione asincrona. Una sfida con la comunicazione asincrona è rappresentata dalla gestione degli errori. Un modo per risolvere questo problema consiste nell'usare un processore del gestore errori separato. Quando il consumer di eventi riscontra un errore, invia immediatamente e in modo asincrono l'evento errato al processore del gestore errori e si sposta. Il processore del gestore errori prova a correggere l'errore e invia di nuovo l'evento al canale di inserimento originale. Tuttavia, se il processore del gestore errori non riesce, può inviare l'evento errato a un amministratore per un'ulteriore ispezione. Se si usa un processore del gestore errori, gli eventi errati vengono elaborati fuori sequenza quando vengono inviati di nuovo.
Perdita di dati.
Un'altra sfida con la comunicazione asincrona è la perdita di dati. Se uno dei componenti si arresta in modo anomalo prima di elaborare correttamente e consegnare l'evento al componente successivo, l'evento viene eliminato e non lo rende mai nella destinazione finale. Per ridurre al minimo la probabilità di perdita di dati, rendere persistenti gli eventi in transito e rimuovere o annullare la coda degli eventi solo quando il componente successivo riconosce la ricezione dell'evento. Queste funzionalità sono note come modalità di riconoscimento client e supporto dell'ultimo partecipante.
Implementazione di un modello tradizionale di richiesta-risposta.
A volte il producer di eventi richiede una risposta immediata da parte del consumer di eventi, ad esempio per ottenere l'idoneità del cliente prima di procedere con un ordine. In un'architettura basata su eventi, è possibile ottenere comunicazioni sincrone usando la messaggistica di richiesta-risposta.
Questo modello viene in genere implementato con due code: una coda di richieste e una coda di risposta. Il producer di eventi invia una richiesta asincrona a una coda di richieste, sospende altre operazioni su tale attività e attende una risposta nella coda di risposta. Questo approccio trasforma questo modello in modo efficace in un processo sincrono. I consumer di eventi elaborano quindi la richiesta e inviano la risposta tramite una coda di risposta. Questo approccio usa in genere un ID sessione per il rilevamento, quindi il producer di eventi sa quale messaggio nella coda di risposta è correlato alla richiesta specifica. La richiesta originale può anche specificare il nome della coda di risposta, potenzialmente temporanea, in un'intestazione reply-to o in un altro attributo personalizzato concordato a vicenda.
Manutenzione del numero appropriato di eventi.
La generazione di un numero eccessivo di eventi con granularità fine può saturare e sovraccaricare il sistema, rendendo difficile analizzare efficacemente il flusso complessivo degli eventi. Questo problema è esacerbato quando è necessario eseguire il rollback delle modifiche. Al contrario, il consolidamento eccessivo degli eventi può anche creare problemi, con conseguente elaborazione e risposte non necessarie da parte dei consumer di eventi.
Per ottenere il giusto equilibrio, prendere in considerazione le conseguenze degli eventi e se i consumer devono esaminare i payload dell'evento per determinare le risposte. Ad esempio, se si dispone di un componente di controllo della conformità, potrebbe essere sufficiente pubblicare solo due tipi di eventi: conforme e non conforme. Questo approccio consente di garantire che ogni evento venga elaborato solo dai consumer pertinenti, che impedisce l'elaborazione non necessaria.
Altre considerazioni
La quantità di dati da includere in un evento può essere una considerazione significativa che influisce sulle prestazioni e sui costi. È possibile semplificare il codice di elaborazione ed eliminare ricerche aggiuntive inserendo tutte le informazioni pertinenti necessarie per l'elaborazione direttamente nell'evento. Quando si aggiunge solo una quantità minima di informazioni a un evento, ad esempio alcuni identificatori, si riducono i tempi di trasporto e i costi. Tuttavia, questo approccio richiede il codice di elaborazione per recuperare eventuali informazioni aggiuntive necessarie. Per altre informazioni, vedi Mettere i tuoi eventi su una dieta.
Una richiesta è visibile solo al componente di gestione delle richieste. Tuttavia, gli eventi sono spesso visibili a più componenti in un carico di lavoro, anche se tali componenti non sono o non sono destinati a utilizzarli. Per operare con una mentalità "presupporre la violazione", tenere presente quali informazioni includere negli eventi per evitare l'esposizione imprevista delle informazioni.
Molte applicazioni usano l'architettura basata su eventi come architettura primaria. È possibile combinare questo approccio con altri stili architetturali per creare un'architettura ibrida. Le combinazioni tipiche includono microservizi e pipe e filtri. Integrare l'architettura guidata dagli eventi per migliorare le prestazioni del sistema eliminando colli di bottiglia e fornendo una pressione elevata durante volumi di richieste elevate.
Domini specifici spesso si estendono su più producer di eventi, consumer o canali di eventi. Le modifiche apportate a un determinato dominio potrebbero influire su molti componenti.
Risorse correlate
- Video di discussione della community sulle considerazioni in merito alla scelta tra coreografia e orchestrazione.