Stile di architettura guidato dagli eventi

Analisi di flusso di Azure
Funzioni di Azure
Bus di servizio di Azure

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.

Diagramma dello stile di un'architettura guidata dagli eventi

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 conosce i consumer in ascolto. Anche i consumer sono separati tra loro e ognuno visualizza tutti gli eventi. Questo comportamento differisce da un modello con consumer concorrenti, in cui i consumer eseguono il pull di messaggi da una coda e un messaggio viene elaborato solo una volta (presupponendo l'assenza di errori). In alcuni sistemi, ad esempio IoT, gli eventi devono essere inseriti a volumi molto elevati.

Un'architettura basata su eventi può usare un modello di pubblicazione/sottoscrizione (anche detto pub/sub) o un modello di flusso di eventi.

  • Pubblicazione/sottoscrizione: l'infrastruttura di messaggistica tiene traccia delle sottoscrizioni. Quando viene pubblicato un evento, il modello lo invia a ogni sottoscrittore. Quando viene ricevuto un evento, non può essere riprodotto e i nuovi sottoscrittori non possono visualizzarlo.

  • Flusso di eventi: gli eventi vengono scritti in un log. Gli eventi sono rigorosamente ordinati (in una partizione) e durevoli. I client non sottoscrivono il flusso, ma un client può invece leggere da qualsiasi parte del flusso. Il client è responsabile di far avanzare la propria posizione nel flusso. Questo significa che un client può aggiungersi in qualsiasi momento e può riprodurre gli eventi.

Sul lato consumer si applicano alcune variazioni comuni:

  • Elaborazione semplice degli eventi. Un evento attiva immediatamente un'azione nel consumer. Ad esempio, è possibile usare Funzioni di Azure con un trigger del bus di servizio, in modo da eseguire una funzione ogni volta che viene pubblicato un messaggio in un argomento del bus di servizio.

  • Correlazione di eventi di base. Un consumer deve elaborare un numero ridotto di eventi aziendali discreti, in genere correlati da un identificatore, per rendere persistenti alcune informazioni degli eventi precedenti da usare durante l'elaborazione di eventi successivi. Questo modello è supportato da librerie come NServiceBus e MassTransit.

  • Elaborazione complessa degli eventi. Un consumer elabora una serie di eventi, cercando i modelli nei dati di evento, tramite una tecnologia come Analisi di flusso di Azure. Ad esempio, è possibile aggregare letture da un dispositivo incorporato in base a un intervallo di tempo e quindi generare una notifica se la media mobile supera una determinata soglia.

  • Elaborazione di flussi di eventi. Usare una piattaforma di flussi di dati, come l'hub IoT di Azure o Apache Kafka, come pipeline per inserire gli eventi e fornirli agli elaboratori di flussi. Gli elaboratori di flussi intervengono per elaborare o trasformare il flusso. Possono essere presenti più elaboratori di flussi per sottosistemi diversi nell'applicazione. Questo approccio è ideale per i carichi di lavoro IoT.

L'origine degli eventi può essere esterna al sistema, ad esempio può essere costituita da dispositivi fisici in una soluzione IoT. In questo caso, il sistema deve essere in grado di inserire i dati in base alla velocità effettiva e al volume richiesti dall'origine dati.

Esistono due approcci principali per strutturare i payload degli eventi. Quando si ha il controllo sui consumer di eventi, prendere questa decisione sulla struttura del payload per consumer; combinazione di approcci in base alle esigenze all'interno di un singolo carico di lavoro.

  • Inclusione di tutti gli attributi necessari nel payload: questo approccio viene usato quando si desidera che i consumer dispongano di tutte le informazioni disponibili senza la necessità di 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.

  • Inclusione di solo 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. Sebbene questo metodo offra una migliore coerenza dei dati a causa di un singolo sistema di record, può comportare prestazioni inferiori rispetto al primo approccio, perché i consumer devono eseguire spesso query sull'origine dati. Esistono meno preoccupazioni relative all'accoppiamento, alla larghezza di banda, alla gestione dei contratti o al controllo delle versioni, perché gli eventi sono più piccoli e contratti più semplici.

Nel diagramma logico precedente ogni tipo di consumer viene visualizzato come una singola casella. In pratica, è normale che siano presenti più istanze di un consumer, per evitare che il consumer diventi un singolo punto di guasto nel sistema. Potrebbero essere necessarie più istanze anche per gestire il volume e la frequenza degli eventi. Un singolo consumer potrebbe inoltre elaborare eventi in più thread. Questa condizione può creare problemi se gli eventi devono essere elaborati in ordine o se richiedono una semantica di tipo exactly-once. Vedere Ridurre al minimo il coordinamento.

Esistono due topologie primarie all'interno di molte architetture guidate dagli eventi:

  • Topologia broker. I componenti trasmettono occorrenze come eventi all'intero sistema e altri componenti agiscono sull'evento o semplicemente lo ignorano. Questa topologia è utile quando il flusso di elaborazione degli eventi è relativamente semplice. Non esiste un coordinamento o un'orchestrazione centrale, quindi questa topologia può essere molto 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 esiste alcun mezzo nativo da riavviare o riprodurre. La gestione degli errori e le strategie di intervento manuali devono essere valutate attentamente, perché questa topologia può essere fonte di incoerenza dei dati.

  • Topologia mediator. Questa topologia risolve alcune delle carenze della topologia broker. Esiste un mediatore di eventi che ne gestisce e controlla il flusso. Il mediatore di eventi mantiene lo stato e gestisce le funzionalità di gestione e riavvio degli errori. A differenza della topologia broker, i componenti trasmettono le occorrenze come comandi e solo ai canali designati, di solito code di messaggi. Questi comandi non devono essere ignorati dai consumer. 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 di eventi potrebbe diventare un collo di bottiglia o un problema di affidabilità.

Quando usare questa architettura

  • Più sottosistemi devono elaborare gli stessi eventi.
  • Elaborazione in tempo reale con ritardo minimo.
  • Elaborazione complessa degli eventi, ad esempio con criteri di ricerca o aggregazione in base a intervalli di tempo.
  • Volume elevato e alta velocità dei dati, ad esempio in sistemi IoT.

Vantaggi

  • I producer e i consumer sono separati.
  • Nessuna integrazione da punto a punto. È facile aggiungere nuovi consumer al sistema.
  • I consumer possono rispondere agli eventi immediatamente, non appena arrivano.
  • 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.

    In genere, ogni tipo di consumer viene eseguito in più istanze, per motivi di resilienza e scalabilità. Questo approccio può rivelarsi problematico se gli eventi devono essere elaborati in ordine (all'interno di un tipo di consumer) o se la logica di elaborazione idempotente dei messaggi non è implementata.

  • Coordinamento dei messaggi tra servizi.

    I processi aziendali spesso implicano la pubblicazione e la sottoscrizione di più servizi ai messaggi per ottenere un risultato coerente in un intero carico di lavoro. Modelli di flusso di lavoro come il modello di coreografia e Orchestrazione Saga si possono usare per gestire in modo affidabile flussi di messaggi tra diversi 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. Pertanto, 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 ha riconosciuto la ricezione dell'evento. Queste funzionalità sono in genere note come modalità di conferma client e supporto dell'ultimo partecipante.

  • Implementazione di un modello tradizionale di richiesta-risposta.

    In alcuni casi, il producer di eventi richiede una risposta immediata da parte del consumer di eventi, ad esempio per ottenere l'idoneità di un cliente prima di procedere con un ordine. Nell'architettura basata su eventi, la comunicazione sincrona può essere ottenuta tramite la messaggistica di richiesta-risposta.

    Questo modello viene in genere implementato usando più code, ovvero una coda di richieste e una coda di risposte. 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; trasformando questo 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.

  • Gestione 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à, può essere sufficiente pubblicare solo due tipi di eventi: conforme e non conforme. Questo approccio consente l'elaborazione di ogni evento solo da parte dei consumer pertinenti, impedendo l'elaborazione non necessaria.

Considerazioni aggiuntive

  • La quantità di dati da includere in un evento può essere una considerazione significativa che influisce sulle prestazioni e sui costi. L'inserimento di tutte le informazioni pertinenti necessarie per l'elaborazione nell'evento stesso può semplificare il codice di elaborazione e salvare ricerche aggiuntive. L'inserimento della quantità minima di informazioni in un evento, ad esempio solo un paio di identificatori, ridurrà il tempo di trasporto e i costi, ma richiede al codice di elaborazione di cercare eventuali informazioni aggiuntive necessarie. Per altre informazioni su questa funzionalità, dare uno sguardo a questo post del blog.
  • Anche se una richiesta è visibile solo al componente di gestione delle richieste, gli eventi sono spesso visibili a più componenti in un carico di lavoro, anche se tali componenti non sono destinati al consumo. Operare con una mentalità "presunzione della violazione", tenere presente le informazioni da includere negli eventi per evitare l'esposizione imprevista delle informazioni.
  • Molte applicazioni usano l'architettura basata su eventi come architettura primaria; Tuttavia, questo approccio può essere combinato con altri stili di architettura, con conseguente architettura ibrida. Le combinazioni comuni includono microservizi e pipe e filtri. L'integrazione di un'architettura basata su eventi migliora le prestazioni del sistema eliminando i colli di bottiglia e fornendo una pressione indietro 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.