ASP.NET Core SignalR hostingu i skalowania

Przez Andrew Stanton-Nurse, Brady Gaster i Tom Dykstra

W tym artykule opisano zagadnienia dotyczące hostowania i skalowania aplikacji o dużym natężeniu ruchu, które używają ASP.NET Core SignalR.

Sesje sticky

SignalR wymaga obsługi wszystkich żądań HTTP dla określonego połączenia przez ten sam proces serwera. W przypadku SignalR uruchamiania w farmie serwerów (wiele serwerów) należy użyć "lepkich sesji". "Sesje sticky" są również nazywane koligacją sesji przez niektóre moduły równoważenia obciążenia. Aplikacja systemu Azure Service używa Routing żądań aplikacji (ARR) do kierowania żądań. Włączenie ustawienia "Koligacja ARR" w usłudze aplikacja systemu Azure spowoduje włączenie "lepkich sesji". Jedynymi okolicznościami, w których sesje sticky nie są wymagane, są:

  1. W przypadku hostowania na jednym serwerze w jednym procesie.
  2. W przypadku korzystania z usługi platformy Azure SignalR .
  3. Gdy wszyscy klienci są skonfigurowani do używania tylko obiektów WebSocket, a ustawienie SkipNegotiation jest włączone w konfiguracji klienta.

We wszystkich innych okolicznościach (w tym w przypadku użycia płaszczyzny wewnętrznej usługi Redis) środowisko serwera musi być skonfigurowane pod kątem sesji sticky.

Aby uzyskać wskazówki dotyczące konfigurowania usługi aplikacja systemu Azure dla SignalRprogramu , zobacz Publikowanie aplikacji ASP.NET Core SignalR w usłudze aplikacja systemu Azure Service. Aby uzyskać wskazówki dotyczące konfigurowania sesji sticky dla Blazor aplikacji korzystających z usługi platformy AzureSignalR, zobacz Hostowanie i wdrażanie aplikacji po stronie Blazor serwera ASP.NET Core.

Zasoby połączenia TCP

Liczba współbieżnych połączeń TCP, które może obsługiwać serwer internetowy, jest ograniczona. Klienci HTTP w warstwie Standardowa używają połączeń efemerycznych . Te połączenia można zamknąć, gdy klient przejdzie w stan bezczynności i zostanie ponownie otwarty później. Z drugiej strony SignalR połączenie jest trwałe. SignalR połączenia pozostają otwarte nawet wtedy, gdy klient przejdzie w stan bezczynności. W aplikacji o dużym natężeniu ruchu, która obsługuje wielu klientów, te trwałe połączenia mogą spowodować, że serwery osiągną maksymalną liczbę połączeń.

Połączenia trwałe zużywają również dodatkową pamięć, aby śledzić każde połączenie.

Duże wykorzystanie zasobów SignalR związanych z połączeniem może mieć wpływ na inne aplikacje internetowe hostowane na tym samym serwerze. Po SignalR otwarciu i przechowywaniu ostatnich dostępnych połączeń TCP inne aplikacje internetowe na tym samym serwerze również nie mają więcej dostępnych połączeń.

Jeśli na serwerze zabraknie połączeń, zobaczysz losowe błędy gniazd i błędy resetowania połączenia. Przykład:

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

Aby zachować SignalR , że użycie zasobów nie powoduje błędów w innych aplikacjach internetowych, uruchom je SignalR na różnych serwerach niż inne aplikacje internetowe.

Aby zapewnić SignalR , że użycie zasobów nie powoduje błędów w SignalR aplikacji, przeprowadź skalowanie w poziomie, aby ograniczyć liczbę połączeń, które musi obsłużyć serwer.

Skalowanie w poziomie

Aplikacja, która używa SignalR , musi śledzić wszystkie połączenia, co powoduje problemy dla farmy serwerów. Dodaj serwer i pobiera nowe połączenia, o których inne serwery nie wiedzą. Na przykład na każdym serwerze na poniższym diagramie nie wiadomo o SignalR połączeniach na innych serwerach. Gdy SignalR na jednym z serwerów chce wysłać komunikat do wszystkich klientów, komunikat jest kierowany tylko do klientów połączonych z tym serwerem.

Scaling SignalR without a backplane

Opcje rozwiązania tego problemu to zaplecze usługi platformy Azure SignalR i bazy danych Redis.

Usługa platformy Azure SignalR

Usługa platformy Azure SignalR działa jako serwer proxy dla ruchu w czasie rzeczywistym i podwaja się jako płaszczyzna wsteczna, gdy aplikacja jest skalowana w poziomie na wielu serwerach. Za każdym razem, gdy klient inicjuje połączenie z serwerem, klient jest przekierowywany w celu nawiązania połączenia z usługą. Proces przedstawiono na poniższym diagramie:

Establishing a connection to the Azure SignalR Service

W rezultacie usługa zarządza wszystkimi połączeniami klienckimi, podczas gdy każdy serwer potrzebuje tylko niewielkiej stałej liczby połączeń z usługą, jak pokazano na poniższym diagramie:

Clients connected to the service, servers connected to the service

Takie podejście do skalowania w poziomie ma kilka zalet w porównaniu z alternatywą backplane redis:

  • Sesje sticky, znane również jako koligacja klienta, nie są wymagane, ponieważ klienci są natychmiast przekierowywani do usługi platformy Azure SignalR podczas nawiązywania połączenia.
  • Aplikacja SignalR może skalować w poziomie na podstawie liczby wysłanych komunikatów, a usługa platformy Azure SignalR skaluje w celu obsługi dowolnej liczby połączeń. Na przykład może istnieć tysiące klientów, ale jeśli wysyła się tylko kilka komunikatów na sekundę, SignalR aplikacja nie będzie musiała skalować w poziomie do wielu serwerów tylko w celu obsługi samych połączeń.
  • Aplikacja SignalR nie będzie używać znacznie więcej zasobów połączenia niż aplikacja internetowa bez SignalRprogramu .

Z tych powodów zalecamy usługę platformy Azure SignalR dla wszystkich aplikacji platformy ASP.NET Core SignalR hostowanych na platformie Azure, w tym usługi App Service, maszyn wirtualnych i kontenerów.

Aby uzyskać więcej informacji, zobacz dokumentację usługi platformy AzureSignalR.

Płyta montażowa Redis

Redis to magazyn klucz-wartość w pamięci, który obsługuje system obsługi komunikatów z modelem publikowania/subskrybowania. SignalR Backplane usługi Redis używa funkcji pub/sub do przekazywania komunikatów do innych serwerów. Gdy klient nawiązuje połączenie, informacje o połączeniu są przekazywane do płaszczyzny wstecznej. Gdy serwer chce wysłać komunikat do wszystkich klientów, wysyła go do płaszczyzny wstecznej. Płaszczyzna wsteczna zna wszystkich połączonych klientów i serwery, na których się znajdują. Wysyła komunikat do wszystkich klientów za pośrednictwem odpowiednich serwerów. Ten proces przedstawiono na poniższym diagramie:

Redis backplane, message sent from one server to all clients

Plan wsteczny usługi Redis jest zalecanym podejściem skalowalnym w poziomie dla aplikacji hostowanych we własnej infrastrukturze. Jeśli między centrum danych a centrum danych platformy Azure występuje znaczne opóźnienie połączenia, usługa platformy Azure SignalR może nie być praktyczną opcją dla aplikacji lokalnych z małym opóźnieniem lub wymaganiami dotyczącymi wysokiej przepływności.

Jak wspomniano wcześniej, zalety usługi Platformy Azure SignalR są wadami planu wstecznego usługi Redis:

  • Sesje sticky, znane również jako koligacja klienta, są wymagane, z wyjątkiem sytuacji, gdy obie z następujących wartości są spełnione:
    • Wszyscy klienci są skonfigurowani do używania tylko obiektów WebSocket.
    • Ustawienie SkipNegotiation jest włączone w konfiguracji klienta. Po zainicjowaniu połączenia na serwerze połączenie musi pozostać na tym serwerze.
  • Aplikacja SignalR musi być skalowana w poziomie na podstawie liczby klientów, nawet jeśli wysyła się kilka komunikatów.
  • Aplikacja SignalR używa znacznie więcej zasobów połączenia niż aplikacja internetowa bez SignalRprogramu .

Ograniczenia usług IIS w systemie operacyjnym klienta systemu Windows

Windows 10 i Windows 8.x to systemy operacyjne klienta. Usługi IIS w systemach operacyjnych klienta mają limit 10 połączeń współbieżnych. SignalRPołączenia są następujące:

  • Przejściowe i często ponownie ustanawiane.
  • Nie są usuwane natychmiast, gdy nie są już używane.

Powyższe warunki sprawiają, że prawdopodobnie osiągnie limit 10 połączeń w systemie operacyjnym klienta. Jeśli system operacyjny klienta jest używany do programowania, zalecamy:

  • Unikaj usług IIS.
  • Użyj Kestrel usługi IIS Express jako obiektów docelowych wdrożenia.

System Linux z serwerem Nginx

Poniżej przedstawiono minimalne wymagane ustawienia umożliwiające włączenie obiektów WebSocket, ServerSentEvents i LongPolling dla elementu 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;
    }
  }
}

W przypadku użycia wielu serwerów zaplecza należy dodać sesje sticky, aby zapobiec SignalR przełączaniu serwerów podczas nawiązywania połączenia. Istnieje wiele sposobów dodawania sesji sticky w Nginx. Poniżej przedstawiono dwie metody w zależności od dostępnych rozwiązań.

Oprócz poprzedniej konfiguracji dodano następujące elementy. W poniższych przykładach backend jest nazwą grupy serwerów.

W programie Nginx Open Source użyj polecenia ip_hash , aby kierować połączenia do serwera na podstawie adresu IP klienta:

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

    ip_hash;
  }
}

W programie Nginx Plus użyj polecenia sticky , aby dodać element cookie do żądań i przypiąć żądania użytkownika do serwera:

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

Na koniec zmień proxy_pass http://localhost:5000 wartość w server sekcji na proxy_pass http://backend.

Aby uzyskać więcej informacji na temat obiektów WebSockets over Nginx, zobacz NGINX as a WebSocket Proxy (Serwer proxy protokołu WebSocket).

Aby uzyskać więcej informacji na temat równoważenia obciążenia i sesji sticky, zobacz Równoważenie obciążenia NGINX.

Aby uzyskać więcej informacji na temat platformy ASP.NET Core z serwerem Nginx, zobacz następujący artykuł:

Dostawcy zaplecza innych firm SignalR

Następne kroki

Aby uzyskać więcej informacji, zobacz następujące zasoby: