Hébergement et mise à l'échelle ASP.NET Core SignalR

Par Ashley Stanton-Nurse, Brady Gaster et Tom Dykstra

Cet article explique les considérations d'hébergement et de mise à l'échelle pour les applications à fort trafic qui utilisent ASP.NET Core SignalR.

Sessions persistantes

SignalR nécessite que le même processus serveur gère toutes les requêtes HTTP pour une connexion spécifique. Lorsque SignalR s’exécute sur une ferme de serveurs (plusieurs serveurs), des sessions persistantes doivent être utilisées. Les « sessions persistantes » sont également appelées affinité de session. Azure App Service utilise Microsoft’ARR (Application Request Routing) pour acheminer les demandes. L’activation du paramètre « Affinité de session » (affinité ARR) dans votre application App Service active les sessions sticky.

Il existe trois scénarios dans lesquels les sessions persistantes ne sont pas requises pour une application :

  • Hébergement sur un seul serveur dans un seul processus
  • À l’aide du service Azure SignalR (les sessions sticky sont activées pour le service, et non pour l’application)
  • Tous les clients sont configurés pour utiliser webSockets uniquement et la configuration du client active SkipNegotiation

Dans tous les autres scénarios (y compris lorsque le backplane Redis est utilisé), l’environnement du serveur doit être configuré pour des sessions persistantes.

Pour obtenir des conseils sur la configuration d'Azure App Service pour SignalR, consultez Publier une application SignalR ASP.NET Core sur Azure App Service. Pour obtenir des conseils sur la configuration de sessions permanentes pour les applications Blazor qui utilisent le service SignalR Azure, consultez Héberger et déployer des applications Blazor ASP.NET Core côté serveur.

Ressources de connexion TCP

Le nombre de connexions TCP simultanées qu'un serveur Web peut prendre en charge est limité. Les clients HTTP Standard utilisent des connexions éphémères. Ces connexions peuvent être fermées lorsque le client devient inactif et rouvertes ultérieurement. En revanche, une connexion SignalR est persistante. les connexions SignalR restent ouvertes même lorsque le client devient inactif. Dans une application à fort trafic qui dessert de nombreux clients, ces connexions persistantes peuvent amener les serveurs à atteindre leur nombre maximal de connexions.

Les connexions persistantes consomment également de la mémoire supplémentaire pour suivre chaque connexion.

L'utilisation intensive des ressources liées à la connexion SignalR peut affecter d'autres applications Web hébergées sur le même serveur. Lorsque SignalR s'ouvre et maintient les dernières connexions TCP disponibles, les autres applications Web sur le même serveur n'ont plus de connexions disponibles.

Si un serveur manque de connexions, vous voyez des erreurs de socket aléatoires et des erreurs de réinitialisation de connexion. Par exemple :

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

Pour éviter que l'utilisation des ressources SignalR ne provoque des erreurs dans d'autres applications Web, exécutez SignalR sur des serveurs différents de ceux de vos autres applications Web.

Pour éviter que l'utilisation des ressources SignalR ne provoque des erreurs dans une application SignalR, effectuez une mise à l'échelle pour limiter le nombre de connexions qu'un serveur doit gérer.

Scale-out

Une application qui utilise SignalR doit garder une trace de toutes ses connexions, ce qui crée des problèmes pour une batterie de serveurs. Ajoutez un serveur et il obtient de nouvelles connexions que les autres serveurs ne connaissent pas. Par exemple, SignalR sur chaque serveur dans le diagramme suivant n’a pas connaissance des connexions sur les autres serveurs. Lorsque SignalR sur un des serveurs veut envoyer un message à tous les clients, le message ne va qu'aux clients connectés à ce serveur.

Illustration illustrant la mise à l’échelle SignalR sans plan arrière.

Les options pour résoudre ce problème sont le service SignalR Azure et le backplane Redis.

Service Azure SignalR

Le service SignalR Azure fonctionne comme un proxy pour le trafic en temps réel et sert également de fond de panier lorsque l'application est déployée sur plusieurs serveurs. Chaque fois qu'un client initie une connexion au serveur, le client est redirigé pour se connecter au service. Le diagramme suivant illustre ce processus :

Illustration qui illustre l’établissement d’une connexion au Azure SignalR Service.

Le résultat est que le service gère toutes les connexions client, tandis que chaque serveur n'a besoin que d'un petit nombre constant de connexions au service, comme illustré dans le schéma suivant :

Illustration illustrant les clients et les serveurs connectés au service.

Cette approche de scale-out présente plusieurs avantages par rapport à l’alternative du backplane Redis :

  • Les sessions persistantes, également appelées affinité client, ne sont pas nécessaires, car les clients sont immédiatement redirigés vers le service Azure SignalR lors de leur connexion.
  • Une application SignalR peut évoluer en fonction du nombre de messages envoyés, tandis que le service SignalR Azure évolue pour gérer n'importe quel nombre de connexions. Par exemple, il peut y avoir des milliers de clients, mais si seulement quelques messages par seconde sont envoyés, l’application SignalR n’a pas besoin d’effectuer un scale-out vers plusieurs serveurs pour gérer les connexions elles-mêmes.
  • Une SignalR application n’utilise pas beaucoup plus de ressources de connexion qu’une application web sans SignalR.

Pour ces raisons, la recommandation consiste à utiliser le service Azure SignalR pour toutes les applications ASP.NET Core SignalR hébergées sur Azure, notamment App Service, les machines virtuelles et les conteneurs.

Pour plus d’informations, consultez la documentation Azure SignalR Service.

Fond de panier Redis

Redis est une base de données clé-valeur en mémoire qui prend en charge un système de messagerie reposant sur un modèle de publication/abonnement. Le backplane Redis SignalR utilise la fonctionnalité de publication/abonnement pour transférer des messages vers d’autres serveurs. Lorsqu’un client se connecte, les informations sur la connexion sont transmises au backplane. Lorsqu’un serveur souhaite envoyer un message à tous les clients, il l’envoie au plan arrière. Le backplane connaît tous les clients connectés et les serveurs sur lesquels ils se trouvent. Il envoie le message à tous les clients via leurs serveurs respectifs. Ce processus est illustré dans le schéma suivant :

Illustration montrant le plan d’arrière Redis avec un message envoyé d’un serveur à tous les clients.

Le backplane Redis est l’approche recommandée pour la mise à l’échelle horizontale des applications hébergées sur votre propre infrastructure. Si une latence de connexion importante existe entre votre centre de données et un centre de données Azure, Azure SignalR Service peut ne pas être une option pratique pour les applications locales avec des exigences de faible latence ou de débit élevé.

Les avantages du service Azure SignalR décrits précédemment sont des inconvénients pour le backplane Redis :

  • Les sessions persistantes, également appelées affinité client, sont requises, sauf lorsque les deux conditions suivantes sont remplies :
    • Tous les clients sont configurés pour utiliser uniquement WebSockets.
    • Le paramètre SkipNegotiation est activé dans la configuration du client. Une fois qu’une connexion est lancée sur un serveur, la connexion doit rester sur ce serveur.
  • Une application SignalR doit effectuer un scale-out en fonction du nombre de clients, même lors de l’envoi de quelques messages.
  • Une SignalR application utilise beaucoup plus de ressources de connexion qu’une application web sans SignalR.

Limitations IIS sur le système d’exploitation client Windows

Windows 10 et Windows 8.x sont des systèmes d'exploitation clients. Internet Information Services (IIS) sur les systèmes d’exploitation clients a une limite de 10 connexions simultanées. Les SignalR connexions présentent les caractéristiques suivantes :

  • Ils sont éphémères et souvent recréés.
  • Ils ne sont pas supprimés immédiatement lorsqu’ils ne sont plus utilisés.

Ces caractéristiques permettent d’atteindre la limite de 10 connexions sur un système d’exploitation client. Lorsque vous utilisez un système d’exploitation client pour le développement, tenez compte des recommandations suivantes :

  • Éviter IIS
  • Utiliser Kestrel ou IIS Express comme cibles de déploiement

Linux avec Nginx

Le code suivant contient les paramètres minimum requis pour activer WebSockets, ServerSentEvents et LongPolling pour 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;
    }
  }
}

Lorsque plusieurs serveurs back-end sont utilisés, des sessions persistantes doivent être mises en place pour empêcher les connexions SignalR de changer de serveur au moment de la connexion. Il existe plusieurs façons d'ajouter des sessions persistantes dans Nginx. Les exemples suivants montrent deux approches basées sur ce que vous avez disponible.

Le code suivant complète l’exemple de configuration précédent. Dans les extraits de code, backend correspond au nom du groupe de serveurs.

  • Avec Nginx Open Source, utilisez cette option ip_hash pour router les connexions vers un serveur en fonction de l’adresse IP du client :

    http {
       upstream backend {
         # App server 1
         server localhost:5000;
         # App server 2
         server localhost:5002;
    
         ip_hash;
       }
    }
    
  • Avec Nginx Plus, utilisez sticky pour ajouter un cookie aux requêtes et épingler les requêtes utilisateur à un serveur :

    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;
       }
    }
    
  • Pour les deux configurations, remplacez proxy_pass http://localhost:5000 par server dans la section proxy_pass http://backend.

Vous trouverez plus d’informations dans Host ASP.NET Core sur Linux avec Nginx.

Autres fournisseurs de backplanes SignalR

Les fournisseurs non Microsoft suivants proposent également des backplanes SignalR :