Introduzione a scale-out in SignalR

di Patrick Fletcher

Avviso

Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.

Versioni software usate in questo argomento

Versioni precedenti di questo argomento

Per informazioni sulle versioni precedenti di SignalR, vedere Versioni precedenti di SignalR.

Domande e commenti

Lasciare commenti e suggerimenti su come è piaciuta questa esercitazione e cosa è possibile migliorare nei commenti nella parte inferiore della pagina. Se si hanno domande che non sono direttamente correlate all'esercitazione, è possibile pubblicarle nel forum di ASP.NET SignalR o StackOverflow.com.

In generale, esistono due modi per ridimensionare un'applicazione Web: aumentare e aumentare le prestazioni.

  • Aumentare le prestazioni significa usare un server più grande (o una macchina virtuale più grande) con più RAM, CPU e così via.
  • L'aumento del numero di istanze implica l'aggiunta di altri server per gestire il carico.

Il problema relativo alla scalabilità verticale è che si raggiunge rapidamente un limite per le dimensioni del computer. Oltre a questo, è necessario aumentare il numero di istanze. Tuttavia, quando si aumenta il numero di istanze, i client possono essere indirizzati a server diversi. Un client connesso a un server non riceverà messaggi inviati da un altro server.

Diagramma che mostra le frecce che passano da Client a Load Balancer ai server.

Una soluzione consiste nell'inoltrare messaggi tra server, usando un componente denominato backplane. Con un backplane abilitato, ogni istanza dell'applicazione invia messaggi al backplane e il backplane li inoltra alle altre istanze dell'applicazione. In elettronica, un backplane è un gruppo di connettori paralleli. Per analogia, un backplane SignalR connette più server.

Diagramma che mostra le frecce che passano dal server app Signal R al backplane al server app Signal R ai computer.

SignalR offre attualmente tre backplan:

  • bus di servizio di Azure. Il bus di servizio è un'infrastruttura di messaggistica che consente ai componenti di inviare messaggi in modo ad accoppiamento libero.
  • Redis. Redis è un archivio chiave-valore in memoria. Redis supporta un modello di pubblicazione/sottoscrizione ("pub/sub") per l'invio di messaggi.
  • SQL Server. Il backplane SQL Server scrive i messaggi nelle tabelle SQL. Il backplane usa Service Broker per la messaggistica efficiente. Tuttavia, funziona anche se Service Broker non è abilitato.

Se si distribuisce l'applicazione in Azure, è consigliabile usare il backplane Redis usando Cache Redis di Azure. Se si esegue la distribuzione nella propria server farm, prendere in considerazione i backplan SQL Server o Redis.

Gli argomenti seguenti contengono esercitazioni dettagliate per ogni backplane:

Implementazione

In SignalR ogni messaggio viene inviato tramite un bus di messaggi. Un bus di messaggi implementa l'interfaccia IMessageBus , che fornisce un'astrazione di pubblicazione/sottoscrizione. Il backplanes funziona sostituendo l'IMessageBus predefinito con un bus progettato per tale backplane. Ad esempio, il bus di messaggi per Redis è RedisMessageBus e usa il meccanismo pub/sub redis per inviare e ricevere messaggi.

Ogni istanza del server si connette al backplane tramite il bus. Quando viene inviato un messaggio, passa al backplane e il backplane lo invia a ogni server. Quando un server riceve un messaggio dal backplane, inserisce il messaggio nella cache locale. Il server recapita quindi i messaggi ai client dalla cache locale.

Per ogni connessione client, lo stato del client durante la lettura del flusso del messaggio viene rilevato usando un cursore. Un cursore rappresenta una posizione nel flusso di messaggi. Se un client si disconnette e quindi si riconnette, chiede al bus tutti i messaggi ricevuti dopo il valore del cursore del client. La stessa cosa accade quando una connessione usa un polling lungo. Al termine di una lunga richiesta di polling, il client apre una nuova connessione e chiede i messaggi arrivati dopo il cursore.

Il meccanismo del cursore funziona anche se un client viene instradato a un server diverso in caso di riconnessione. Il backplane è a conoscenza di tutti i server e non importa a quale server si connette un client.

Limitazioni

Usando un backplane, la velocità effettiva massima dei messaggi è inferiore a quella in cui i client comunicano direttamente con un singolo nodo server. Questo perché il backplane inoltra ogni messaggio a ogni nodo, quindi il backplane può diventare un collo di bottiglia. Se questa limitazione è un problema dipende dall'applicazione. Ecco ad esempio alcuni scenari tipici di SignalR:

  • Trasmissione server (ad esempio, ticker azionario): i backplan funzionano correttamente per questo scenario, perché il server controlla la frequenza con cui vengono inviati i messaggi.
  • Da client a client (ad esempio, chat): in questo scenario, il backplane potrebbe essere un collo di bottiglia se il numero di messaggi viene ridimensionato con il numero di client; ovvero, se la frequenza dei messaggi aumenta proporzionalmente man mano che un numero maggiore di client viene aggiunto.
  • Tempo reale ad alta frequenza (ad esempio, giochi in tempo reale): un backplane non è consigliato per questo scenario.

Abilitazione della traccia per il ridimensionamento di SignalR

Per abilitare la traccia per i backplan, aggiungere le sezioni seguenti al file web.config, sotto l'elemento di configurazione radice:

<configuration>
  <system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.ServiceBusMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.ScaleoutMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
      <!-- Off, Critical, Error, Warning, Information, Verbose -->
    </switches>
    <sharedListeners>
      <add name="SignalR-Bus" 
          type="System.Diagnostics.TextWriterTraceListener" 
          initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
  . . .
</configuration>