Résilience et reprise d’activité après sinistre dans Azure SignalR Service

La résilience et la reprise d’activité après sinistre sont des besoins communs des systèmes en ligne. Azure SignalR Service fournit déjà une disponibilité de 99,9 %, mais il demeure un service régional. En cas de panne à l’échelle d’une région, votre instance de service ne bascule pas vers une autre région dans la mesure où elle s’exécute toujours dans la même région.

Pour la récupération d’urgence régionale, nous recommandons les deux approches suivantes :

  • Activer la géoréplication (méthode simple). Cette fonctionnalité gère automatiquement le basculement régional. Quand vous activez cette fonctionnalité, il n’y a qu’une seule instance Azure SignalR et aucune modification de code n’est introduite. Pour plus d’informations, consultez cet article sur la géoréplication.
  • Utiliser plusieurs points de terminaison dans le Kit de développement logiciel (SDK) du service. Le kit SDK de notre service prend en charge plusieurs instances du service SignalR et bascule automatiquement vers d’autres instances lorsque certaines d’entre elles ne sont pas disponibles. Cette fonctionnalité vous permet de récupérer en cas de sinistre, mais vous devez configurer vous-même la bonne topologie du système. Ce document vous explique comment faire.

Architecture à haute disponibilité pour le service SignalR

Pour assurer la résilience inter-région du service SignalR, vous devez configurer plusieurs instances du service dans différentes régions. Par conséquent, lorsqu’une région est défaillante, les autres peuvent être utilisées comme sauvegarde. Lorsque les serveurs d’applications se connectent à plusieurs instances du service, deux rôles existent : principal et secondaire. Le rôle principal est une instance chargée de recevoir le trafic en ligne, tandis que le rôle secondaire sert d’instance de secours entièrement fonctionnelle. Dans notre implémentation du kit SDK, la négociation retourne uniquement les points de terminaison principaux. Ainsi, les clients ne se connectent qu’aux points de terminaison principaux dans le cadre d’un fonctionnement normal. Mais lorsque l’instance principale est défaillante, la négociation retourne les points de terminaison secondaires pour permettre aux clients de continuer à se connecter. L’instance principale et le serveur d’applications sont connectés via des connexions serveur normales, mais l’instance secondaire et le serveur d’applications sont connectés via un type spécial de connexion, appelée connexion faible. L’une des caractéristiques d’une connexion faible est qu’elle est incapable d’accepter le routage de la connexion du client en raison de l’emplacement de l’instance secondaire dans une autre région. Le routage d’un client vers une autre région n’est pas un choix optimal, car il augmente la latence.

Une instance de service peut avoir différents rôles si elle se connecte à plusieurs serveurs d’applications. Une installation type pour un scénario inter-région consiste à avoir deux paires ou plus d’instances de service SignalR et de serveurs d’applications. Dans chaque paire, le serveur d’applications et le service SignalR se trouvent dans la même région, et le service SignalR est connecté au serveur d’applications en tant que rôle principal. Entre deux paires, le serveur d’applications et le service SignalR sont également connectés, mais le service SignalR devient une instance secondaire lorsqu’il se connecte au serveur dans une autre région.

Avec cette topologie, un message provenant d’un serveur peut être remis à tous les clients, car tous les serveurs d’applications et toutes les instances du service SignalR sont interconnectés. Mais lorsqu’un client est connecté, il est routé vers le serveur d’applications dans la même région pour obtenir une latence réseau optimale.

Le diagramme suivant illustre cette topologie :

Le diagramme montre deux régions, chacune avec un serveur d’applications et un service SignalR, où chaque serveur est associé au service SignalR dans sa région comme principal et avec le service dans l’autre région comme secondaire.

Configurer plusieurs instances de SignalR Service

Plusieurs instances de SignalR Service sont prises en charge sur les serveurs d'applications et Azure Functions.

Une fois que SignalR Service et les serveurs d'applications/Azure Functions ont été créés dans chaque région, vous pouvez configurer vos serveurs d'applications/Azure Functions pour qu'ils se connectent à toutes les instances de SignalR Service.

Via la configuration

Normalement, vous savez déjà comment définir la chaîne de connexion du service SignalR grâce à des variables d’environnement/paramètres d’application/web.config, dans une entrée de configuration nommée Azure:SignalR:ConnectionString. Si vous avez plusieurs points de terminaison, vous pouvez les définir dans plusieurs entrées de configuration, chacune dans le format suivant :

Azure:SignalR:ConnectionString:<name>:<role>

Dans ConnectionString, <name> est le nom du point de terminaison et <role> est son rôle (principal ou secondaire). Le nom est facultatif, mais il est utile si vous souhaitez personnaliser davantage le comportement de routage entre plusieurs points de terminaison.

Via le code

Si vous préférez stocker les chaînes de connexion à un autre endroit, vous pouvez également les lire dans votre code et les utiliser comme paramètres lorsque vous appelez AddAzureSignalR() (dans ASP.NET Core) ou MapAzureSignalR() (dans ASP.NET).

Voici l’exemple de code :

ASP.NET Core :

services.AddSignalR()
        .AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
        {
            new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
            new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
        });

ASP.NET :

app.MapAzureSignalR(GetType().FullName, hub,  options => options.Endpoints = new ServiceEndpoint[]
    {
        new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
        new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
    };

Il est possible de configurer plusieurs instances principales ou secondaires, S’il existe plusieurs instances principales et/ou secondaires, la négociation retourne un point de terminaison dans l’ordre suivant :

  1. S’il y a au moins une instance principale en ligne, retourner une instance principale en ligne aléatoire.
  2. Si toutes les instances principales sont hors service, retourner une instance secondaire en ligne aléatoire.

Pour les liaisons SignalR Azure Functions

Pour activer plusieurs instances SignalR Service, vous devez :

  1. Utiliser le type de transport Persistent.

    Le type de transport par défaut est le mode Transient. Vous devez ajouter l’entrée suivante à votre fichier local.settings.json ou au paramètre d’application sur Azure.

    {
        "AzureSignalRServiceTransportType":"Persistent"
    }
    

    Remarque

    Lorsque vous passez du mode Transient au mode Persistent, vous pouvez constater un changement de comportement de la sérialisation JSON. En effet, dans le mode Transient, la bibliothèque Newtonsoft.Json est utilisée pour sérialiser les arguments des méthodes de hub, alors que dans le mode Persistent, la bibliothèque System.Text.Json est utilisée par défaut. System.Text.Json présente certaines différences clés dans le comportement par défaut avec Newtonsoft.Json. Si vous souhaitez utiliser Newtonsoft.Json en mode Persistent, vous pouvez ajouter un élément de configuration : "Azure:SignalR:HubProtocol":"NewtonsoftJson" dans le fichier local.settings.json ou Azure__SignalR__HubProtocol=NewtonsoftJson sur le Portail Azure.

  2. Configurer plusieurs entrées de points de terminaison SignalR Service dans votre configuration.

    Nous utilisons un objet ServiceEndpoint pour représenter une instance SignalR Service. Vous pouvez définir un point de terminaison de service avec son <EndpointName> et son <EndpointType> dans la clé d’entrée, et la chaîne de connexion dans la valeur d’entrée. Les clés sont au format suivant :

    Azure:SignalR:Endpoints:<EndpointName>:<EndpointType>
    

    <EndpointType> est facultatif et est défini par défaut sur primary. Consultez les exemples ci-dessous :

    {
        "Azure:SignalR:Endpoints:EastUs":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:EastUs2:Secondary":"<ConnectionString>",
    
        "Azure:SignalR:Endpoints:WestUs:Primary":"<ConnectionString>"
    }
    

    Remarque

    • Lorsque vous configurez des points de terminaison Azure SignalR dans App Service sur le Portail Azure, n’oubliez pas de remplacer ":" par "__" (double trait de soulignement dans les clés). Pour savoir pourquoi, consultez Variables d’environnement.

    • La chaîne de connexion configurée avec la clé {ConnectionStringSetting} (par défaut, « AzureSignalRConnectionString ») est également reconnue comme point de terminaison de service principal avec un nom vide. Toutefois, ce style de configuration n’est pas recommandé pour plusieurs points de terminaison.

Pour le kit SDK Management

Ajouter plusieurs points de terminaison à partir de la configuration

Effectuez la configuration avec la clé Azure:SignalR:Endpoints pour la chaîne de connexion SignalR Service. La clé doit être au format Azure:SignalR:Endpoints:{Name}:{EndpointType}, où Name et EndpointType sont des propriétés de l’objet ServiceEndpoint et sont accessibles à partir du code.

Vous pouvez ajouter plusieurs chaînes de connexion d’instance à l’aide des commandes dotnet suivantes :

dotnet user-secrets set Azure:SignalR:Endpoints:east-region-a <ConnectionString1>
dotnet user-secrets set Azure:SignalR:Endpoints:east-region-b:primary <ConnectionString2>
dotnet user-secrets set Azure:SignalR:Endpoints:backup:secondary <ConnectionString3>

Ajouter plusieurs points de terminaison à partir du code

Une classe ServiceEndpoint décrit les propriétés d’un point de terminaison Azure SignalR Service. Vous pouvez configurer plusieurs points de terminaison d’instance quand vous utilisez le kit SDK Azure SignalR Management à l’aide du code suivant :

var serviceManager = new ServiceManagerBuilder()
                    .WithOptions(option =>
                    {
                        options.Endpoints = new ServiceEndpoint[]
                        {
                            // Note: this is just a demonstration of how to set options.Endpoints
                            // Having ConnectionStrings explicitly set inside the code is not encouraged
                            // You can fetch it from a safe place such as Azure KeyVault
                            new ServiceEndpoint("<ConnectionString0>"),
                            new ServiceEndpoint("<ConnectionString1>", type: EndpointType.Primary, name: "east-region-a"),
                            new ServiceEndpoint("<ConnectionString2>", type: EndpointType.Primary, name: "east-region-b"),
                            new ServiceEndpoint("<ConnectionString3>", type: EndpointType.Secondary, name: "backup"),
                        };
                    })
                    .BuildServiceManager();

Bonne pratique et séquence de basculement

Vous disposez maintenant de la configuration appropriée de la topologie du système. Chaque fois qu’une instance du service SignalR est défaillante, le trafic en ligne est acheminé vers d’autres instances. Voici ce qui se passe lorsqu’une instance principale est défaillante (et est restaurée plus tard) :

  1. L’instance de service principale est défaillante. Toutes les connexions serveur sur cette instance sont supprimées.
  2. Tous les serveurs connectés à cette instance la marquent comme hors connexion. La négociation cesse de retourner ce point de terminaison et commence à retourner le point de terminaison secondaire.
  3. Toutes les connexions client sur cette instance sont également fermées, puis les clients se reconnectent. Dans la mesure où les serveurs d’applications retournent maintenant un point de terminaison secondaire, les clients se connectent à l’instance secondaire.
  4. L’instance secondaire prend désormais en charge tout le trafic en ligne. Tous les messages du serveur aux clients peuvent encore être remis, car l’instance secondaire est connectée à tous les serveurs d’applications. Cependant, les messages des clients au serveur sont routés uniquement au serveur d’applications figurant dans la même région.
  5. Une fois l’instance principale restaurée et de nouveau en ligne, le serveur d’applications rétablit les connexions à cette dernière et la marque comme étant en ligne. La négociation retourne désormais à nouveau le point de terminaison principal, si bien que les nouveaux clients sont reconnectés à l’instance principale. En revanche, les clients existants ne sont pas supprimés et continuent d’être routés vers l’instance secondaire jusqu’à ce qu’ils se déconnectent eux-mêmes.

Les diagrammes ci-dessous illustrent le basculement dans le service SignalR :

Figure 1. Avant le basculement Avant le basculement

Figure 2. Après le basculement Après le basculement

Figure 3. Peu de temps après la récupération de l’instance principale Peu de temps après la récupération de l’instance principale

Dans le cadre du fonctionnement normal, seuls le serveur d’applications et le service SignalR principaux gèrent le trafic en ligne (en bleu). Après le basculement, le serveur d’applications et le service SignalR secondaires deviennent également actifs. Une fois le service SignalR principal à nouveau en ligne, les nouveaux clients se connectent au service SignalR principal. Cependant, les clients existants restent connectés à l’instance secondaire, de sorte que les deux instances gèrent le trafic. Une fois que tous les clients existants se sont déconnectés, votre système revient à la normale (Figure 1).

Il existe deux modèles principaux pour implémenter une architecture inter-région à haute disponibilité :

  1. Le premier consiste à avoir une paire de serveur d’applications et d’instance de service SignalR gérant tout le trafic en ligne, et à avoir une autre paire de secours (configuration active/passive, illustrée à la Figure 1).
  2. L’autre consiste à avoir deux paires (ou plus) de serveurs d’applications et d’instances du service SignalR, gérant chacune une part du trafic en ligne et servant de sauvegarde pour les autres paires (configuration active/active, semblable à la Fig. 3).

Le service SignalR peut prendre en charge ces deux modèles, la principale différence étant la façon dont vous implémentez les serveurs d’applications. Si les serveurs d’applications sont en mode actif/passif, le service SignalR est également en mode actif/passif (car le serveur d’applications principal retourne uniquement son instance de service SignalR principale). Si les serveurs d’applications sont en mode actif/actif, le service SignalR est également en mode actif/actif (car tous les serveurs d’applications retournent leurs propres instances SignalR principales afin qu’ils puissent tous obtenir le trafic).

Quel que soit le modèle que vous choisissez d’utiliser, notez que vous devez connecter chaque instance de service SignalR à un serveur d’applications en tant qu’instance principale.

De plus, en raison de la nature de la connexion SignalR (connexion longue), les clients sont confrontés à des interruptions de connexion en cas de sinistre et de basculement. Vous devez gérer de telles situations côté client pour les rendre transparentes pour vos clients finaux. Par exemple, rétablissez une connexion qui a été fermée.

Comment tester un basculement

Effectuez les étapes suivantes pour déclencher le basculement :

  1. Sous l’onglet Mise en réseau de la ressource principale dans le portail, désactivez l’accès au réseau public. Si un réseau privé est activé pour la ressource, utilisez des règles de contrôle d’accès pour refuser tout le trafic.
  2. Redémarrez la ressource principale.

Étapes suivantes

Dans cet article, vous avez appris à configurer votre application pour assurer la résilience du service SignalR. Pour plus de détails sur la connexion serveur/client et le routage des connexions dans le service SignalR, vous pouvez lire cet article sur les éléments internes du service SignalR.

Pour les scénarios de mise à l’échelle, comme le partitionnement qui utilise conjointement plusieurs instances pour gérer un grand nombre de connexions, lisez l’article sur la mise à l’échelle de plusieurs instances.

Pour plus d'informations sur la configuration d'Azure Functions avec plusieurs instances de SignalR Service, consultez Prise en charge de plusieurs instances d'Azure SignalR Service dans Azure Functions.