hosting e scalabilità di ASP.NET Core SignalR

Di Andrew Stanton-Nurse, Brady Gaster e Tom Dykstra

Questo articolo illustra le considerazioni sull'hosting e sul ridimensionamento per le app a traffico elevato che usano ASP.NET Core SignalR.

Sessioni permanenti

SignalR richiede che tutte le richieste HTTP per una connessione specifica vengano gestite dallo stesso processo del server. Quando SignalR è in esecuzione in una server farm (più server), è necessario usare le "sessioni permanenti". Le "sessioni permanenti" sono dette anche affinità di sessione. uso del servizio app AzureApplication Request Routing (ARR) per instradare le richieste. L'abilitazione dell'impostazione "Affinità di sessione" (affinità ARR) nel servizio app Azure abilita le "sessioni permanenti". Le uniche circostanze in cui le sessioni permanenti non sono necessarie per l'app sono:

  1. Quando si ospita in un singolo server in un singolo processo.
  2. Quando si usa il servizio di Azure SignalR (le sessioni permanenti sono abilitate per il servizio, non per l'app).
  3. Quando tutti i client sono configurati per l'uso solo di WebSocket e l'impostazione SkipNegotiation è abilitata nella configurazione client.

In tutte le altre circostanze (incluso quando viene usato il backplane Redis), l'ambiente server deve essere configurato per le sessioni permanenti.

Per indicazioni sulla configurazione del servizio app Azure per SignalR, vedere Pubblicare un'app core SignalR ASP.NET nel servizio app Azure. Per indicazioni sulla configurazione di sessioni permanenti per Blazor le app che usano il servizio di AzureSignalR, vedere Ospitare e distribuire ASP.NET app sul lato Blazor server Core.

Risorse di connessione TCP

Il numero di connessioni TCP simultanee supportate da un server Web è limitato. I client HTTP standard usano connessioni temporanee . Queste connessioni possono essere chiuse quando il client diventa inattiva e riaperto in un secondo momento. D'altra parte, una SignalR connessione è persistente. SignalR le connessioni rimangono aperte anche quando il client diventa inattiva. In un'app a traffico elevato che serve molti client, queste connessioni persistenti possono causare il raggiungimento del numero massimo di connessioni dei server.

Le connessioni permanenti utilizzano anche memoria aggiuntiva per tenere traccia di ogni connessione.

L'uso elevato delle risorse SignalR correlate alla connessione può influire su altre app Web ospitate nello stesso server. Quando SignalR si apre e contiene le ultime connessioni TCP disponibili, anche altre app Web nello stesso server non dispongono di altre connessioni.

Se un server esaurisce le connessioni, verranno visualizzati errori casuali del socket e errori di reimpostazione della connessione. Ad esempio:

An attempt was made to access a socket in a way forbidden by its access permissions...

Per evitare SignalR che l'utilizzo delle risorse causi errori in altre app Web, eseguire SignalR su server diversi rispetto alle altre app Web.

Per evitare SignalR che l'utilizzo delle risorse causi errori in un'app SignalR , aumentare il numero di connessioni che un server deve gestire.

Aumentare il numero di istanze

Un'app che usa SignalR deve tenere traccia di tutte le connessioni, che crea problemi per una server farm. Aggiungere un server e ottenere nuove connessioni che gli altri server non conoscono. Ad esempio, SignalR in ogni server del diagramma seguente non è a conoscenza delle connessioni negli altri server. Quando SignalR in uno dei server vuole inviare un messaggio a tutti i client, il messaggio passa solo ai client connessi a tale server.

Ridimensionamento SignalR senza backplane

Le opzioni per risolvere questo problema sono il backplane del servizio di Azure SignalR e Redis.

Servizio di Azure SignalR

Il servizio di Azure SignalR funziona come proxy per il traffico in tempo reale e raddoppia come backplane quando l'app viene ridimensionata in più server. Ogni volta che un client avvia una connessione al server, il client viene reindirizzato per connettersi al servizio. Il processo è illustrato nel diagramma seguente:

Stabilire una connessione al servizio di Azure SignalR

Il risultato è che il servizio gestisce tutte le connessioni client, mentre ogni server necessita solo di un numero costante ridotto di connessioni al servizio, come illustrato nel diagramma seguente:

Client connessi al servizio, server connessi al servizio

Questo approccio per la scalabilità orizzontale presenta diversi vantaggi rispetto all'alternativa backplane redis:

  • Le sessioni permanenti, note anche come affinità client, non sono necessarie, perché i client vengono immediatamente reindirizzati al servizio di Azure SignalR quando si connettono.
  • Un'app SignalR può aumentare il numero di istanze in base al numero di messaggi inviati, mentre il servizio di Azure SignalR viene ridimensionato per gestire un numero qualsiasi di connessioni. Ad esempio, potrebbero esserci migliaia di client, ma se vengono inviati solo pochi messaggi al secondo, l'app SignalR non dovrà aumentare il numero di istanze a più server solo per gestire le connessioni stesse.
  • Un'app SignalR non userà molte più risorse di connessione rispetto a un'app Web senza SignalR.

Per questi motivi, è consigliabile usare il servizio di Azure SignalR per tutte le app core SignalR ASP.NET ospitate in Azure, tra cui servizio app, macchine virtuali e contenitori.

Per altre informazioni, vedere la documentazione del servizio di AzureSignalR.

Backplane Redis

Redis è un archivio chiave-valore in memoria che supporta un sistema di messaggistica con un modello di pubblicazione/sottoscrizione. Il SignalR backplane Redis usa la funzionalità pub/sub per inoltrare i messaggi ad altri server. Quando un client effettua una connessione, le informazioni di connessione vengono passate al backplane. Quando un server vuole inviare un messaggio a tutti i client, invia al backplane. Il backplane conosce tutti i client connessi e i server in cui si trovano. Invia il messaggio a tutti i client tramite i rispettivi server. Questo processo è illustrato nel diagramma seguente:

Backplane Redis, messaggio inviato da un server a tutti i client

Il backplane Redis è l'approccio di scalabilità orizzontale consigliato per le app ospitate nella propria infrastruttura. Se esiste una latenza di connessione significativa tra il data center e un data center di Azure, il servizio di Azure SignalR potrebbe non essere un'opzione pratica per le app locali con bassa latenza o requisiti di velocità effettiva elevata.

I vantaggi del servizio di Azure SignalR indicati in precedenza sono svantaggi per il backplane Redis:

  • Le sessioni permanenti, note anche come affinità client, sono obbligatorie, tranne quando entrambe le seguenti sono vere:
    • Tutti i client sono configurati per l'uso solo di WebSocket.
    • L'impostazione SkipNegotiation è abilitata nella configurazione client. Dopo l'avvio di una connessione in un server, la connessione deve rimanere su tale server.
  • Un'app SignalR deve aumentare il numero di istanze in base al numero di client anche se vengono inviati pochi messaggi.
  • Un'app SignalR usa molte più risorse di connessione rispetto a un'app Web senza SignalR.

Limitazioni di IIS nel sistema operativo client Windows

Windows 10 e Windows 8.x sono sistemi operativi client. IIS nei sistemi operativi client ha un limite di 10 connessioni simultanee. SignalRLe connessioni sono:

  • Ripristino temporaneo e frequente.
  • Non eliminato immediatamente quando non viene più usato.

Le condizioni precedenti rendono probabile che venga raggiunto il limite di 10 connessioni in un sistema operativo client. Quando viene usato un sistema operativo client per lo sviluppo, è consigliabile:

  • Evitare IIS.
  • Usare Kestrel o IIS Express come destinazioni di distribuzione.

Linux con Nginx

Di seguito sono riportate le impostazioni minime necessarie per abilitare WebSockets, ServerSentEvents e LongPolling per SignalR:

http {
  map $http_connection $connection_upgrade {
    "~*Upgrade" $http_connection;
    default keep-alive;
  }

  server {
    listen 80;
    server_name example.com *.example.com;

    # Configure the SignalR Endpoint
    location /hubroute {
      # App server url
      proxy_pass http://localhost:5000;

      # Configuration for WebSockets
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_cache off;
      # WebSockets were implemented after http/1.0
      proxy_http_version 1.1;

      # Configuration for ServerSentEvents
      proxy_buffering off;

      # Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds
      proxy_read_timeout 100s;

      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    }
  }
}

Quando si usano più server back-end, è necessario aggiungere sessioni permanenti per impedire SignalR alle connessioni di cambiare server durante la connessione. Esistono diversi modi per aggiungere sessioni permanenti in Nginx. Di seguito sono illustrati due approcci a seconda di ciò che è disponibile.

Oltre alla configurazione precedente, viene aggiunto quanto segue. Negli esempi seguenti è backend il nome del gruppo di server.

Con Nginx Open Source, usare ip_hash per instradare le connessioni a un server in base all'indirizzo IP del client:

http {
  upstream backend {
    # App server 1
    server localhost:5000;
    # App server 2
    server localhost:5002;

    ip_hash;
  }
}

Con Nginx Plus, usare sticky per aggiungere un cookie oggetto alle richieste e aggiungere le richieste dell'utente a un server:

http {
  upstream backend {
    # App server 1
    server localhost:5000;
    # App server 2
    server localhost:5002;

    sticky cookie srv_id expires=max domain=.example.com path=/ httponly;
  }
}

Infine, modificare proxy_pass http://localhost:5000 nella server sezione in proxy_pass http://backend.

Per altre informazioni sui WebSocket su Nginx, vedere NGINX come proxy WebSocket.

Per altre informazioni sul bilanciamento del carico e sulle sessioni permanenti, vedere Bilanciamento del carico NGINX.

Per altre informazioni su ASP.NET Core con Nginx, vedere l'articolo seguente:

Provider backplane di terze parti SignalR

Passaggi successivi

Per ulteriori informazioni, vedi le seguenti risorse: