Azure SignalR Service

Pull Based e Push Based

Immaginate di avere un’app di messaggistica che necessita del vostro intervento manuale per poter ricevere i messaggi, senza una funzionalità real-time; ci troveremmo davanti alla prima app di messaggistica “quasi” istantanea il cui insuccesso sarebbe scontatissimo. 🙂 Una delle problematiche principali nello sviluppo di applicazioni (mobile o web che siano) è infatti avere i dati aggiornati in tempo reale.

Per sua natura, il protocollo HTTP non ha stato. Questo implica che ogni volta che il client effettua una richiesta al server, quest’ultimo chiude la connessione dopo averla fornita ed il client è obbligato a fare una nuova richiesta per avere altri dati di cui necessita; si tratta di un protocollo di tipo pull-based, ovvero è il client che richiede dati e ogni volta contatta il server per averli.

Al contrario, invece, nel push-based è il server che invia i dati al client senza che quest’ultimo effettui richieste specifiche.

SignalR fa proprio questo, ovvero consente di aggiungere funzionalità real-time ad applicazioni web creando un canale bidirezionale sempre attivo tra client (browser) e server (Web App). La potenza di SignalR sta soprattutto nel fatto che lo sviluppatore non deve preoccuparsi di quale canale di comunicazione utilizzare, in quanto sarà SignalR ad occuparsene, impostando di volta in volta la connessione più idonea in base alle caratteristiche del server e dei client.

Un po’ di storia

SignalR nasce nel 2011 da due sviluppatori, Microsoft David Fowler and Damian Edwards. Fino a quel momento per risolvere il problema della comunicazione in tempo reale venivano utilizzate tecniche poco efficienti come polling continui effettuati con AJAX oppure eventi server che spesso non erano implementati del tutto sui browser. Ovviamente, vista la semplicità con cui SignalR gestiva queste problematiche in modo invisibile allo sviluppatore e la facilità con cui poteva essere implementato il tutto, fu un grande successo.

La versione sviluppata per .NET Core è invece del 2018, ed è stata rilasciata all’interno di .NET Core 2.1; in questa occasione SignalR è stato completamente riscritto e rispetto al suo predecessore presenta alcune differenze, tra le quali:

  • non supporta la riconnessione automatica del client nel caso in cui la connessione sia stata interrotta, dovrà essere obbligatoriamente gestita manualmente
  • la libreria ASP.NET Core SignalR Client è stata totalmente riscritta in Typescript
  • la libreria client non è più disponibile all’interno di nuget, ma è possibile scaricarla direttamente da npm
  • è stata rimossa la dipendenza a jQuery
  • compatibilità con Internet Explorer >= 11
  • non utilizza più il metodo di trasporto Forever Frame
  • supporto per Azure SignalR Service

Scelta del Canale di Comunicazione

Come detto in precedenza, è SignalR che determina quale sia il miglior canale di comunicazione in base alle caratteristiche del server e del client. I metodi di trasporto che utilizza SignalR sono:

  • HTTP Long Polling
  • Forever Frame (solo IE e non .NET Core SignalR)
  • Server-Sent Events
  • Web Sockets

Ma quale è la logica che utilizza SignalR?

Aiutiamoci con questo albero decisionale:

Negoziazione

La richiesta POST [endpoint-base]/negotiate è eseguita dal client verso il server e ha lo scopo di connettersi a quest’ultimo.

La risposta del server può essere di tre tipi:

1. Una risposta in formato JSON contenente la ConnectionId (utilizzata per identificare la connessione tra server e client) e la lista dei metodi di trasporto supportati dal server.


2. Una risposta di reindirizzamento che comunica al client quale URL utilizzare per connettersi e quale token utilizzare (quest’ultimo è un valore opzionale).

3. Una risposta che contiene un errore e che dovrebbe interrompere nuovi tentativi di connessione.

HUB

SignalR utilizza gli hub per comunicare tra Client e Server.

L’hub è l’oggetto principale che andremo ad utilizzare per poter interagire con i client e contiene tutti quei metodi che possono essere invocati da remoto attraverso Javascript.

Grazie a SignalR il server può inviare messaggi in modo distinto ai vari user.

Il server ha diverse possibilità per l’invio dei messaggi; può inviare un messaggio:

  • a tutti i client sottoscritti
  • solo ad un determinato client
  • solo ad un gruppo di client
  • a un gruppo di client eccetto uno specifico

SignalR utilizza come protocollo di default JSON. Di conseguenza le comunicazioni effettuate tra Server e Client sono serializzate in questo formato. Per velocizzare la comunicazione, è possibile usare un ulteriore protocollo supportato da SignalR, ovvero MessagePack. MessagePack è un formato di serializzazione binaria più compatto e veloce rispetto a JSON, come si può vedere dall’esempio:

Nell’esempio sopra, nel primo box è presente un JSON di 24 bytes, mentre ne secondo è presente lo stesso JSON serializzato con MessagePack e pesante solo 15 bytes, con un risparmio del 63%.

Ma quando arriviamo a Azure SignalR?

Ci siamo quasi, ma prima poniamo l’accento su una domanda. Qual è il guadagno nell’utilizzare il servizio fornito da Azure anziché implementare SignalR direttamente a codice? Per farla breve, scalabilità.

Nel caso la vostra applicazione web inizi ad avere un traffico considerevole, dovrete necessariamente gestire SignalR per far fronte a questo aumento. Sarà quindi opportuno investire del tempo per migliorare l’infrastruttura e alle volte questo tempo potreste non averlo.

Utilizzando invece Azure SignalR, questo problema non si pone, in quanto sarà l’infrastruttura di Azure ad occuparsene in modo trasparente allo sviluppatore.

Se state già utilizzando SignalR e volete utilizzare il servizio Azure sarà un passaggio semplice ed indolore, come vedremo più avanti.

È arrivato il momento di scrivere una semplice applicazione che utilizzi Azure SignalR; analizziamola passo-passo. In questo esempio creeremo un sistema per simulare gli ordini di prodotti in una caffetteria.

L’interfaccia presenterà un form per scegliere il tipo di prodotto e la dimensione della tazza. Una volta scelti e premuto il bottone “Ordina”, verrà fatta una chiamata POST ad un controller dedicato, il quale invierà un messaggio a tutti i client collegati informandoli del nuovo ordine. Chi ha effettuato l’ordine, invece, vedrà in tempo reale l’avanzamento, fino alla conferma di consegna.

NB: Potrete trovare questo progetto completo su GitHub [https://github.com/render93/GiunecoTech.AzureSignalrService], mentre qui di seguito saranno evidenziate solo le caratteristiche salienti.

Creiamo innanzitutto una nuova solution, con all’interno un progetto MVC Core. Per utilizzare SignalR avremmo bisogno del pacchetto Nuget Microsoft.AspNetCore.SignalR.

Startup.cs

Per prima cosa apriamo il file Startup.cs e andiamo ad aggiornare i metodi ConfigureServices e Configure.

Nel primo metodo andiamo ad aggiungere quanto segue – ciò che ho inserito all’interno della lambda permette di poter visualizzare in modo più dettagliato eventuali errori di SignalR, il mio consiglio è di usarlo solo in debug e rimuoverlo in release tramite la direttiva #if DEBUG

Nel secondo metodo andiamo ad inserire alla pipeline SignalR specificando quale sarà l’hub e l’endpoint con cui comunicare.

SignalR.js

Per permettere il corretto funzionamento di SignalR nel browser è necessario scaricare la libreria SignalR.js e configurare SignalR in modo che si colleghi alla sua controparte server.

Viene impostata la connessione al server andando a specificare l’endpoint con il quale saranno gestiti i vari messaggi. Successivamente, sempre all’interno della stessa funzione, possiamo andare ad inserire tutti i messaggi che il server andrà a richiamare, descrivendone il comportamento. Vediamone un estratto:

Quando il server chiamerà i messaggi sopra, la relativa funzione verrà invocata ed eseguirà determinate azioni. Nel caso specifico, quando il server invierà il messaggio “NewClientConnected”, comparirà una notifica a schermo a tutti i client connessi all’hub, ad eccezione di chi ha effettuato l’accesso.

HUB

Il prossimo step è la creazione dell’hub. La nostra classe dovrà derivare dall’oggetto “Hub”. All’interno di questo oggetto ci sono due metodi: GetUpdateForOrder e OnConnectedAsync, quest’ultimo è un override.

Il primo metodo viene evocato una volta che si seleziona il bottone “Ordina” e il suo scopo è quello di simulare l’avanzamento dell’ordine effettuato. I messaggi riguardanti l’avanzamento dell’ordine vengono recapitati solo a colui che ha effettuato l’ordine.

Il secondo metodo, invece effettua due azioni distinte:

La prima è quella di impostare la connectionId solo per il chiamante del metodo. Questo significa che il messaggio di “ConnectionStarted” sarà recapitato solo a chi ha effettuato la connessione all’hub. Il tutto è possibile grazie alla proprietà Caller.

La seconda azione, invece, invia un messaggio a tutti i client connessi ad eccezione di chi ha scatenato l’invocazione del metodo OnConnectedAsync (proprietà Others). L’azione che provoca il messaggio “NewClientConnected” è quella che abbiamo visto poco prima, ha quindi senso che chi scatena il metodo non veda comparire la notifica.

Avviando il sito web su due tab differenti possiamo vedere come, alla selezione di una bevanda, nel secondo tab viene notificato un nuovo ordine, mentre nel primo tab viene mostrato l’avanzamento dello stato dell’ordine in tempo reale.

Passiamo ad Azure SignalR

Fino a questo momento abbiamo utilizzato semplicemente SignalR, ma non il servizio fornito da Azure. È arrivato il momento di effettuare il passaggio. L’operazione è molto semplice.

Prima di tutto, rechiamoci sul sito di Azure, andiamo su “Tutti i servizi” e ricerchiamo “SignalR”. Nella schermata che compare facciamo click su Aggiungi e proseguiamo.

In questa schermata inseriamo le informazioni necessarie per la creazione di SignalR.

Oltre alle informazioni relative alla sottoscrizione, Gruppo e nome risorsa e regione concentriamoci sulle ultime voci;

Pricing Tier

Questa voce la selezione di due opzioni: Free e Standard. La prima voce permette di gestire una singola unità con al massimo 20 connessioni concorrenti e 20K di messaggi al giorno. La seconda gestisce 1K di connessioni concorrenti e 1M di messaggi al giorno per unità. È possibile scalare fino a 100 unità per singola istanza. Questa seconda opzione è a pagamento.

Per unità si intende un gruppo di connessioni.

Unit Count

Nel caso scegliate Standard come Pricing Tier, dovrete selezionare il numero di unità.

Per maggiori informazioni potete vedere la documentazione qui. [https://github.com/Azure/azure-signalr/blob/dev/docs/faq.md#service-mode].

Terminata la creazione del servizio, recatevi nella pagina “Keys”, sotto ad Impostazioni, e copiate la connection string, che useremo più tardi.

Per effettuare il passaggio ad Azure SignalR avremo bisogno del pacchetto Nuget Microsoft.Azure.SignalR.

Startup.cs

Andiamo a operare sul file Startup.cs e modifichiamo i metodi ConfigureServices e Configure come segue (il vecchio codice è commentato per vedere le differenze):

La costante ConnectionString contiene la stringa di connessione che abbiamo copiato prima.

Nessun’altra modifica al codice, tutto il resto rimane invariato.

Quando viene chiamato [endpoint-base]/negotiate, sarà restituirà un url e un access token (punto 2 del paragrafo relativo alla Negoziazione), al quale il client dovrà connettersi.

Lascia un commento

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