Azure Event Hubs

Ovvero, come gestire migliaia di eventi al secondo

Inviare un messaggio da un dispositivo ad un altro sembra un compito piuttosto semplice, ed effettivamente lo sarebbe… Ma se i messaggi che devono essere inviati diventano milioni e la frequenza con cui devono essere inviati diventa molto alta, allora il compito potrebbe non essere più così elementare. Inoltre, per mantenere un risultato soddisfacente, dobbiamo garantire che questi messaggi vengano inviati in modo sicuro ed affidabile.

Fino a qualche anno fa questa condizione non rappresentava un problema urgente, poiché i messaggi da inviare tra vari dispositivi erano relativamente pochi e con bassa frequenza. Ma al giorno d’oggi, con l’avvento della Internet of Things (IoT), i dispositivi possono inviare messaggi in modo continuo. Pensate, ad esempio, a sensori che rilevano la temperatura, la pressione, o “semplicemente” ad un’auto a guida autonoma: centinaia di migliaia di messaggi che vengono inviati e processati.

Quale potrebbe essere una soluzione affidabile che riesca a gestire una mole così alta di informazioni?


Avete mai sentito parlare di broker di messaggi?

Un broker di messaggi è un’applicazione che si posiziona tra due sistemi software che comunicano e si scambiano informazioni, al fine di garantire una comunicazione agevole. L’invio dei messaggi avviene su un canale in cui è il broker ad essere il responsabile della consegna del messaggio ai soggetti interessati.

Gli altri attori coinvolti sono:

  • Publisher: colui che trasmette gli eventi al broker e non conosce i destinatari.
  • Subscriber: colui che si sottoscrive al broker ed effettua un polling continuo.

Ci sono diversi broker di messaggi disponibili, sia open source che a pagamento.
Tra i tanti vi segnalo in particolare:

  • RabbitMQ
  • Amazon Kinesis
  • Google Pub/Sub
  • Azure Event Hubs

AZURE EVENT HUB

Partiamo dalla definizione che troviamo sulla pagina ufficiale: “Hub eventi di Azure è un servizio di pubblicazione-sottoscrizione altamente scalabile in grado di inserire milioni di eventi al secondo e di trasmetterli a più applicazioni consentendo di elaborare e analizzare quantità molto elevate di dati prodotti dalle applicazioni e dai dispositivi connessi.”

La funzione di Azure Event Hub è semplice: memorizzare eventi e renderli disponibili a chi ne fa richiesta. Tutto questo è fatto tramite un endpoint REST che viene messo a disposizione per raggiungere tale scopo. L’endpoint è facilmente raggiungibile da qualsiasi dispositivo via HTTPS, AMQS (Advanced Message Queuing Protocol) oppure Kafka 1.0 e successivi. Azure Event Hubs riesce a gestire un flusso enorme di eventi, garantendo sicurezza e performance.

FUNZIONAMENTO GENERALE E COMPONENTI CHIAVE

Gli Event Producer sono quelle entità che inviano i dati all’Hub tramite HTTPS, AMQS oppure Kafka.

Gli eventi vengono salvati in partizioni specifiche in modo che gli Event Receivers, tramite AMQP 1.0, possano acquisirli.

PARTIZIONI

Particolare attenzione è da porre sulle partizioni. Una partizione è una sequenza ordinata di eventi che si svolge in un hub. Quando arrivano nuovi eventi, vengono aggiunti alla coda di questa sequenza.

I messaggi entrano in una o un’altra partizione, in due modi:

  • specificando la proprietà PartitionKey che possiede l’evento (permettendo a più eventi di essere memorizzati nella stessa partizione);
  • tramite logica round robin (se avete bisogno di delucidazioni su questa tecnica, potete leggere questa documentazione).

TIP: La proprietà PartitionKey può essere usata in modo “intelligente”; essendo conosciuta dal mittente, potrebbe essere utilizzata per raggruppare eventi con logiche specifiche. Ad esempio impostarla con lo stato da cui è stato inviato l’evento (in formato ISO 3166-1, ad esempio).

Azure Event Hubs conserva i dati per un tempo configurabile ed applicabile a tutte le partizioni nell’hub eventi. Gli eventi scadono su base temporale; non possono essere eliminati esplicitamente. Poiché le partizioni sono indipendenti e contengono una propria sequenza di dati, spesso crescono a ritmi diversi.

Questo garantisce buone performance e non deve essere considerato come un limite, in quanto questi dati possono dare un valore aggiunto solo se processati e salvati su altri sistemi di storicizzazione. L’Hub permette quindi agli eventi di transitare temporaneamente in attesa di essere processati.

Il numero di partizioni configurabili è compreso tra 2 e 32 e non è modificabile successivamente, quindi occorre pensare attentamente al numero da inserire in fase di configurazione dell’hub.

Le partizioni sono riempite con una sequenza di dati di eventi che contengono il corpo dell’evento, una lista di proprietà definite dall’utente e alcuni metadata.

Adesso passiamo alla pratica:

Innanzitutto abbiamo bisogno di un account Azure. Se non lo avete già, potere iscrivervi per sfruttare 12 mesi gratuiti di servizi Azure (con alcune limitazioni). Nel caso in cui siate sottoscrittori di Visual Studio avete fino a 130 euro mensili per sperimentare tutti i servizi di Azure.

DISCLAIMER: Come alle scale in Harry Potter, anche ad Azure piace cambiare. Il procedimento mostrato sotto potrebbe differire leggermente da quello che dovrete effettuare voi.

CREAZIONE DELL’EVENT HUB

Creiamo lo Spazio dei Nomi, che sarà il contenitore che accoglierà il nostro Hub di eventi. Per fare questo:

Tutte le risorse (menu a sinistra) -> Aggiungi -> Scriviamo nella barra di ricerca “Event Hubs” e facciamo click su Aggiungi.

Successivamente fare click su Hub Eventi, ed inserire le informazioni richieste.

Vi verrà chiesto il numero di partizioni da inserire e per quanti giorni conservare i messaggi.

Fate click su Criteri di Accesso Condiviso ed inserite i dati richiesti. Questo sarà “l’utente” che utilizzeremo per inviare e ricevere messaggi. Per comodità ne ho creato uno che fa entrambe le azioni, ma potete crearne anche due diversi.

Ogni utente avrà la sua stringa di connessione che utilizzeremo in seguito.

L’Hub eventi necessita anche di uno spazio di archiviazione per poter conservare i messaggi. Azure Event Hubs sfrutta Azure Blob per fare ciò, quindi dovremo creare un Blob.

Tutte le risorse -> Aggiungi -> Scriviamo nella barra di ricerca “Storage account” e facciamo click su Crea.

Inseriamo le informazioni richieste e al termine clicchiamo su Blob.

Successivamente creiamo un Blob cliccando su Contenitore

Creiamo il nostro Blob e copiamo le chiavi di accesso che sono reperibili facendo click su Chiavi di Accesso (ci serviranno dopo).

Adesso abbiamo tutto quello che ci serve, quindi creiamo un nuovo progetto in Visual Studio e selezioniamo Console Application (.Net Core)

NB: Potrete trovare questo progetto completo su GitHub, mentre qui di seguito saranno evidenziate solo le caratteristiche salienti.

Per utilizzare Event Hub abbiamo bisogno di due pacchetti Nuget:

  • Microsoft.Azure.EventHubs
  • Microsoft.Azure.EventHubs.Processor

Creeremo due classi: Publisher.cs e Consumer.cs ed infine nel nostro Program.cs assembleremo il tutto.

PUBLISHER.CS

Questa è la classe deputata ad inviare i messaggi all’hub. Ha due metodi:

  • Init che tramite la stringa di connessione dell’utente (Publisher, nel nostro caso) crea una nuova connessione all’hub eventi.
  • Publish che effettua la serializzazione del messaggio da inviare, la conversione in un array di byte e il successivo invio all’hub – La conversione in array di byte deve essere fatta utilizzando la codifica UTF-8.

Vediamo nel dettaglio quest’ultimo metodo.

Al di là della semplice gestione dell’errore, è possibile vedere come ogni singolo messaggio viene processato e trasformato in un oggetto di tipo EventData, prima di essere inviato all’hub tramite il metodo SendAsync().

È possibile pubblicare più eventi in batch. Per fare questo è necessario istanziare un oggetto EventDataBatch, e, dopo aver aggiunto tutti gli eventi, utilizzarlo come parametro nel metodo SendAsync. Ecco l’esempio:

CONSUMER.CS

Questa è la classe deputata a rimanere in ascolto dell’hub e ricevere i messaggi inviati dai publisher.

La classe deve ereditare dall’interfaccia IEventProcessor. Dovremo implementare quattro metodi:

  • OpenAsync(PartitionContext context): metodo chiamato quando viene stabilita la connessione all’hub
  • Task CloseAsync(PartitionContext context, CloseReason reason): metodo chiamato quando viene chiusa la connessione all’hub. Viene indicato il motivo della chiusura attraverso l’enum CloseReason.
  • ProcessErrorAsync(PartitionContext context, Exception error): metodo chiamato nel caso in cui il client rileva un errore durante la ricezione dell’evento. L’errore viene gestito autonomamente dall’hub. Questo metodo serve solo a scopo informativo in quanto nell’oggetto error è presente l’eccezione ricevuta.
  • Task ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages): metodo chiamato quando viene ricevuto un messaggio dall’hub.

Mi vorrei concentrare su quest’ultimo metodo. Vediamone il codice:

Per prima cosa il messaggio viene decodificato e deserializzato (in questo caso in un oggetto chiamato Device). Successivamente viene banalmente mostrato a video.

Il controllo che vedete in fondo ha lo scopo di impostare un checkpoint. Nel caso in cui, per qualsiasi motivo, l’applicazione fallisca, una volta riavviata il processamento degli eventi riprenderà dall’ultimo checkpoint salvato (nel caso specifico il checkpoint è impostato ogni 5 minuti).

PROGRAM.CS

È il momento di assemblare il tutto!

Viene instanziato il nostro Publisher a cui viene passata la stringa di connessione. Successivamente, tramite un metodo specifico, vengono generati eventi random ed inviati all’hub.

L’ultima parte, invece, è relativa alla configurazione del Consumer.
Viene istanziato l’oggetto chiamato EventProcessorHost. Questo oggetto rappresenta l’host che esegue l’elaborazione dei messaggi ricevuti e necessita di conoscere con quale logica eseguire l’elaborazione, la connessione, la disconnessione e la visualizzazione degli errori nel caso in cui si presentino. Questa è la descrizione del Consumer che abbiamo visto poco fa.

Grazie al metodo RegisterEventProcessorAsync chiuso proprio su Consumer, comunicheremo all’EventProcessorHost come comportarsi.

Infine, tramite UnregisterEventProcessorAsync, chiuderemo la connessione dell’host all’hub.

RIDIMENSIONAMENTO

Azure gestisce automaticamete lo scaling delle partizioni, in modo da bilanciare nel migliore dei modi la mole di dati che dovrà gestire.

Vi invito a scaricare il progetto da GitHub per provare in prima persona.

Troverete due console application:

Avviate inizialmente AnotherConsumer e il consumer si metterà in ascolto utilizzando entrambe le partizioni. Adesso avviate ConsoleApp: verranno inviati gli eventi e, al termine, verrà avviato un altro consumer che sarà in ascolto. Automaticamente in AnotherConsumer verrà disconnesso l’ascolto da una delle partizioni che sarà, invece, messa in ascolto su ConsoleApp. Ecco il bilanciamento automatico gestito dall’SDK di Azure.

EVENT HUB VS SERVICE BUS

Arrivati a questo punto potrebbe sorgere spontanea una domanda: che differenza c’è con Service Bus?

Event Hub è stato progettato per gestire migliaia di eventi al secondo: pensate ad esempio a dati di telemetria. Migliaia di dati al secondo vengono inviati ed è necessaria una infrastruttura solida che riesca a riceverne così tanti in così poco tempo.

Service Bus, invece, viene utilizzato per gestire eventi che hanno bisogno di essere coerenti e in scenari dove sia necessario sapere se l’evento sia andato a buon fine e, in caso negativo, di riuscire a gestire la situazione al meglio. A titolo di esempio, in un ecommerce, per la gestione di una transazione, è altamente consigliato l’utilizzo di Service Bus.


Spero che questa introduzione ad Azure Event Hubs possa aiutarvi nel raggiungere la padronanza degli strumenti necessari per poter gestire al meglio migliaia di eventi che, in un prossimo futuro, potreste essere chiamati a elaborare.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *