SignalR ASP.NET Core Hosting und Skalierung

Von Andrew Stanton-Nurse, Brady Gaster und Tom Dykstra

In diesem Artikel werden Hosting- und Skalierungsüberlegungen für Apps mit hohem Datenverkehr erläutert, die ASP.NET Core SignalRverwenden.

Sticky-Sitzungen

SignalR erfordert, dass alle HTTP-Anforderungen für eine bestimmte Verbindung von demselben Serverprozess behandelt werden. Wenn SignalR sie auf einer Serverfarm (mehrere Server) ausgeführt wird, müssen "Sticky-Sitzungen" verwendet werden. "Sticky Sessions" werden auch von einigen Lastenausgleichern als Sitzungsaffinität bezeichnet. Azure App Service verwendet Anwendungsanforderungsrouting (ARR) zum Weiterleiten von Anforderungen. Das Aktivieren der Einstellung "ARR Affinity" in Ihrem Azure App Service aktiviert "Sticky Sessions". Die einzigen Umstände, in denen klebige Sitzungen nicht erforderlich sind:

  1. Beim Hosten auf einem einzelnen Server in einem einzigen Prozess.
  2. Wenn Sie den Azure-Dienst SignalR verwenden.
  3. Wenn alle Clients nur Für die Verwendung von WebSockets konfiguriert sind und dieEinstellung "SkipNegotiation " in der Clientkonfiguration aktiviert ist.

Unter allen anderen Umständen (einschließlich der Verwendung der Redis backplane), muss die Serverumgebung für Sticky-Sitzungen konfiguriert werden.

Anleitungen zum Konfigurieren von Azure App Service finden SignalRSie unter Veröffentlichen einer ASP.NET Core SignalR App für Azure App Service. Anleitungen zum Konfigurieren von Sticky-Sitzungen für Blazor Apps, die den Azure-Dienst SignalRverwenden, finden Sie unter Hosten und Bereitstellen ASP.NET CoreBlazor Server.

TCP-Verbindungsressourcen

Die Anzahl gleichzeitiger TCP-Verbindungen, die ein Webserver unterstützen kann, ist begrenzt. Standard-HTTP-Clients verwenden ephemerale Verbindungen. Diese Verbindungen können geschlossen werden, wenn der Client leer geht und später erneut geöffnet wird. Andererseits ist eine SignalR Verbindung beständig. SignalR Verbindungen bleiben auch dann geöffnet, wenn der Client leer geht. In einer High-Traffic-App, die vielen Clients dient, können diese beständigen Verbindungen dazu führen, dass Server ihre maximale Anzahl von Verbindungen treffen.

Persistente Verbindungen nutzen auch einen zusätzlichen Arbeitsspeicher, um jede Verbindung nachzuverfolgen.

Die schwere Verwendung von verbindungsbezogenen Ressourcen kann SignalR sich auf andere Web-Apps auswirken, die auf demselben Server gehostet werden. Wenn SignalR Sie die letzten verfügbaren TCP-Verbindungen öffnen und halten, haben andere Web-Apps auf demselben Server auch keine weiteren Verbindungen zur Verfügung.

Wenn ein Server verbindungen ausläuft, werden zufällige Socketfehler und Verbindungsrücksetzungsfehler angezeigt. Beispiel:

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

Um die Ressourcennutzung zu vermeiden SignalR , dass Fehler in anderen Web-Apps verursacht werden, führen Sie SignalR auf verschiedenen Servern als Ihre anderen Web-Apps aus.

Um die Ressourcennutzung zu vermeiden SignalR , dass Fehler in einer SignalR App verursacht werden, skalieren Sie, um die Anzahl der Verbindungen zu beschränken, die ein Server behandeln muss.

Aufskalieren

Eine App, die SignalR verwendet wird, muss alle Verbindungen nachverfolgen, die Probleme für eine Serverfarm erstellt. Fügen Sie einen Server hinzu, und es erhält neue Verbindungen, die die anderen Server nicht kennen. SignalR Beispielsweise ist auf jedem Server im folgenden Diagramm keine Kenntnis der Verbindungen auf den anderen Servern. Wenn SignalR auf einem der Server eine Nachricht an alle Clients gesendet werden soll, geht die Nachricht nur an die Clients, die mit diesem Server verbunden sind.

Skalierung SignalR ohne Backplane

Die Optionen für die Lösung dieses Problems sind der Azure SignalR Service und Redis Backplane.

Azure SignalR Service

Der Azure-Dienst SignalR funktioniert als Proxy für Echtzeitdatenverkehr und doppeltippt als Backplane, wenn die App auf mehreren Servern skaliert wird. Jedes Mal, wenn ein Client eine Verbindung mit dem Server initiiert, wird der Client umgeleitet, um eine Verbindung mit dem Dienst herzustellen. Der Prozess wird durch das folgende Diagramm veranschaulicht:

Einrichten einer Verbindung mit dem Azure-Dienst SignalR

Das Ergebnis ist, dass der Dienst alle Clientverbindungen verwaltet, während jeder Server nur eine kleine konstante Anzahl von Verbindungen mit dem Dienst benötigt, wie im folgenden Diagramm dargestellt:

Clients, die mit dem Dienst verbunden sind, Server, die mit dem Dienst verbunden sind

Dieser Ansatz zum Skalieren hat mehrere Vorteile über die Redis-Backplane-Alternative:

  • Sticky-Sitzungen, die auch als Clientaffinität bezeichnet werden, sind nicht erforderlich, da Clients sofort an den Azure-Dienst SignalR weitergeleitet werden, wenn sie eine Verbindung herstellen.
  • Eine SignalR App kann basierend auf der Anzahl der gesendeten Nachrichten skaliert werden, während der Azure-Dienst SignalR eine beliebige Anzahl von Verbindungen verarbeitet. Beispielsweise könnte es Tausende von Clients geben, aber wenn nur wenige Nachrichten pro Sekunde gesendet werden, muss die SignalR App nicht auf mehrere Server skaliert werden, um die Verbindungen selbst zu behandeln.
  • Eine SignalR App verwendet keine erheblich mehr Verbindungsressourcen als eine Web-App ohne SignalR.

Aus diesen Gründen empfehlen wir den Azure-Dienst SignalR für alle in Azure gehosteten ASP.NET Core-Apps, einschließlich App ServiceSignalR, VMs und Containern.

Weitere Informationen finden Sie in der Azure SignalR Service-Dokumentation.

Redis-Backplane

Redis ist ein speicherinterner Schlüsselwertspeicher, der ein Messagingsystem mit einem Veröffentlichungs-/Abonnementmodell unterstützt. Die SignalR Redis-Backplane verwendet das Pub/Sub-Feature, um Nachrichten an andere Server weiterleiten zu können. Wenn ein Client eine Verbindung erstellt, wird die Verbindungsinformationen an den Backplan übergeben. Wenn ein Server eine Nachricht an alle Clients senden möchte, sendet er an das Backplane. Die Backplane kennt alle verbundenen Clients und welche Server sie aktivieren. Es sendet die Nachricht an alle Clients über ihre jeweiligen Server. Dieser Prozess wird im folgenden Diagramm veranschaulicht:

Redis backplane, Nachricht, die von einem Server an alle Clients gesendet wurde

Die Redis-Backplane ist der empfohlene Skalierungsansatz für Apps, die in Ihrer eigenen Infrastruktur gehostet werden. Wenn eine erhebliche Verbindungslatenz zwischen Ihrem Rechenzentrum und einem Azure-Rechenzentrum besteht, ist Azure SignalR Service möglicherweise keine praktische Option für lokale Apps mit geringer Latenz oder hohen Durchsatzanforderungen.

Die zuvor aufgeführten Azure SignalR Service-Vorteile sind Nachteile für die Redis-Backplane:

  • Sticky-Sitzungen, die auch als Clientaffinität bezeichnet werden, sind erforderlich, außer wenn beides wahr sind:
    • Alle Clients sind nur für die Verwendung von WebSockets konfiguriert.
    • Die Einstellung "SkipNegotiation" ist in der Clientkonfiguration aktiviert. Sobald eine Verbindung auf einem Server initiiert wird, muss die Verbindung auf diesem Server bleiben.
  • Eine SignalR App muss basierend auf der Anzahl der Clients skaliert werden, auch wenn wenige Nachrichten gesendet werden.
  • Eine SignalR App verwendet deutlich mehr Verbindungsressourcen als eine Web-App ohne SignalR.

IIS-Einschränkungen für Windows-Clientbetriebssystem

Windows 10 und Windows 8.x sind Clientbetriebssysteme. IIS für Clientbetriebssysteme verfügt über einen Grenzwert von 10 gleichzeitigen Verbindungen. SignalRDie Verbindungen sind:

  • Vorübergehend und häufig neu eingerichtet.
  • Nicht sofort entsorgt, wenn er nicht mehr verwendet wird.

Die vorhergehenden Bedingungen machen es wahrscheinlich, dass die 10 Verbindungsgrenze für ein Clientbetriebssystem erreicht wird. Wenn ein Clientbetriebssystem für die Entwicklung verwendet wird, empfehlen wir:

  • Vermeiden Sie IIS.
  • Verwenden Kestrel oder IIS Express als Bereitstellungsziele.

Linux mit Nginx

Im Folgenden finden Sie die mindest erforderlichen Einstellungen zum Aktivieren von WebSockets, ServerSentEvents und LongPolling für 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;
    }
  }
}

Wenn mehrere Back-End-Server verwendet werden, müssen sticky-Sitzungen hinzugefügt werden, um Verbindungen beim Herstellen einer Verbindung zu verhindern SignalR . Es gibt mehrere Möglichkeiten zum Hinzufügen von Klebsitzungen in Nginx. Zwei Ansätze werden unten angezeigt, je nachdem, was Sie verfügbar haben.

Das Folgende wird zusätzlich zur vorherigen Konfiguration hinzugefügt. In den folgenden Beispielen backend handelt es sich um den Namen der Gruppe von Servern.

Mit Nginx Open Source können Sie ip_hash Verbindungen auf Basis der IP-Adresse des Clients an einen Server weiterleiten:

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

    ip_hash;
  }
}

Mit Nginx Plus können Sie sticky anforderungen hinzufügen cookie und die Anforderungen des Benutzers an einen Server anheften:

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;
  }
}

Ändern Sie proxy_pass http://localhost:5000 schließlich den server Abschnitt in proxy_pass http://backend.

Weitere Informationen zu WebSockets über Nginx finden Sie unter NGINX als WebSocket-Proxy.

Weitere Informationen zu Lastenausgleichs- und Stickysitzungen finden Sie unter NGINX-Lastenausgleich.

Weitere Informationen zu ASP.NET Core mit Nginx finden Sie im folgenden Artikel:

Drittanbieter-Backplaneanbieter SignalR

Nächste Schritte

Weitere Informationen finden Sie in den folgenden Ressourcen: