Mettre à l’échelle SignalR Service avec plusieurs instances

Le SDK de SignalR Service prend en charge plusieurs points de terminaison pour les instances de SignalR Service. Vous pouvez utiliser cette fonctionnalité pour la messagerie multirégion ou pour mettre à l’échelle des connexions simultanées.

Pour ASP.NET Core

Ajouter plusieurs points de terminaison à partir de la configuration

Configurez avec la clé Azure:SignalR:ConnectionString ou Azure:SignalR:ConnectionString: pour signalR Service chaîne de connexion.

Si la clé commence par Azure:SignalR:ConnectionString:, elle doit être au format Azure:SignalR:ConnectionString:{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:ConnectionString:east-region-a <ConnectionString1>
dotnet user-secrets set Azure:SignalR:ConnectionString:east-region-b:primary <ConnectionString2>
dotnet user-secrets set Azure:SignalR:ConnectionString:backup:secondary <ConnectionString3>

Ajouter plusieurs points de terminaison à partir du code

Une ServicEndpoint classe 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 SDK de Service Azure SignalR au moyen du code suivant :

services.AddSignalR()
        .AddAzureSignalR(options => 
        {
            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"),
            };
        });

Personnaliser le routeur de point de terminaison

Par défaut, le SDK utilise le DefaultEndpointRouter pour sélectionner les points de terminaison.

Comportement par défaut

  1. Routage des demandes du client :

    Quand le client négocie (/negotiate) avec le serveur d’applications, par défaut, le SDK sélectionne de manière aléatoire un point de terminaison dans l’ensemble de points de terminaison de service disponibles.

  2. Routage des messages serveur :

    Lors de l’envoi d’un message à une connexion spécifique et que la connexion cible est acheminée vers le serveur actuel, le message passe directement à ce point de terminaison connecté. Sinon, les messages sont diffusés à chaque point de terminaison Azure SignalR.

Personnaliser l’algorithme de routage

Vous pouvez créer votre propre routeur si vous avez suffisamment de connaissances pour identifier les points de terminaison vers lesquels les messages doivent être envoyés.

L’exemple suivant définit un routeur personnalisé qui route les messages avec un groupe commençant par east- le point de terminaison nommé east:

private class CustomRouter : EndpointRouterDecorator
{
    public override IEnumerable<ServiceEndpoint> GetEndpointsForGroup(string groupName, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the group broadcast behavior, if the group name starts with "east-", only send messages to endpoints inside east
        if (groupName.StartsWith("east-"))
        {
            return endpoints.Where(e => e.Name.StartsWith("east-"));
        }

        return base.GetEndpointsForGroup(groupName, endpoints);
    }
}

L’exemple suivant remplace le comportement de négociation par défaut et sélectionne le point de terminaison en fonction de l’emplacement du serveur d’applications.

private class CustomRouter : EndpointRouterDecorator
{    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (endpointName.Count == 0)
        {
            context.Response.StatusCode = 400;
            var response = Encoding.UTF8.GetBytes("Invalid request");
            context.Response.Body.Write(response, 0, response.Length);
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

N’oubliez pas d’inscrire le routeur sur le conteneur d’injection de dépendances comme suit :

services.AddSingleton(typeof(IEndpointRouter), typeof(CustomRouter));
services.AddSignalR()
        .AddAzureSignalR(
            options => 
            {
                options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
            });

Pour ASP.NET

Ajouter plusieurs points de terminaison à partir de la configuration

Configuration avec la clé Azure:SignalR:ConnectionString ou Azure:SignalR:ConnectionString: pour signalR Service chaîne de connexion.

Si la clé commence par Azure:SignalR:ConnectionString:, elle doit être au format Azure:SignalR:ConnectionString:{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 à web.config :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Azure:SignalR:ConnectionString" connectionString="<ConnectionString1>"/>
    <add name="Azure:SignalR:ConnectionString:en-us" connectionString="<ConnectionString2>"/>
    <add name="Azure:SignalR:ConnectionString:zh-cn:secondary" connectionString="<ConnectionString3>"/>
    <add name="Azure:SignalR:ConnectionString:Backup:secondary" connectionString="<ConnectionString4>"/>
  </connectionStrings>
  ...
</configuration>

Ajouter plusieurs points de terminaison à partir du code

Une ServiceEndpoint classe 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 SDK de Service Azure SignalR au moyen du code suivant :

app.MapAzureSignalR(
    this.GetType().FullName, 
    options => {
            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("<ConnectionString1>"),
                new ServiceEndpoint("<ConnectionString2>"),
                new ServiceEndpoint("<ConnectionString3>"),
            }
        });

Personnaliser un routeur

La seule différence entre ASP.NET SignalR et ASP.NET Core SignalR est le type de contexte http pour GetNegotiateEndpoint. Pour ASP.NET SignalR, il s’agit du type IOwinContext.

Le code suivant est un exemple de négociation personnalisé pour ASP.NET SignalR :

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(IOwinContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        // Override the negotiate behavior to get the endpoint from query string
        var endpointName = context.Request.Query["endpoint"];
        if (string.IsNullOrEmpty(endpointName))
        {
            context.Response.StatusCode = 400;
            context.Response.Write("Invalid request.");
            return null;
        }

        return endpoints.FirstOrDefault(s => s.Name == endpointName && s.Online) // Get the endpoint with name matching the incoming request
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

N’oubliez pas d’inscrire le routeur sur le conteneur d’injection de dépendances comme suit :

var hub = new HubConfiguration();
var router = new CustomRouter();
hub.Resolver.Register(typeof(IEndpointRouter), () => router);
app.MapAzureSignalR(GetType().FullName, hub, options => {
    options.Endpoints = new ServiceEndpoint[]
                {
                    new ServiceEndpoint(name: "east", connectionString: "<connectionString1>"),
                    new ServiceEndpoint(name: "west", connectionString: "<connectionString2>"),
                    new ServiceEndpoint("<connectionString3>")
                };
});

Métriques de point de terminaison de service

Pour activer un routeur avancé, le Kit de développement logiciel (SDK) du serveur SignalR fournit plusieurs métriques pour aider le serveur à prendre des décisions intelligentes. Les propriétés sont sous ServiceEndpoint.EndpointMetrics.

Nom de métrique Description
ClientConnectionCount Nombre total de connexions clientes simultanées sur tous les hubs pour le point de terminaison de service
ServerConnectionCount Nombre total de connexions de serveur simultanées sur tous les hubs pour le point de terminaison de service
ConnectionCapacity Quota total de connexions pour le point de terminaison de service, y compris les connexions client et serveur

Le code suivant est un exemple de personnalisation d’un routeur en fonction de ClientConnectionCount.

private class CustomRouter : EndpointRouterDecorator
{
    public override ServiceEndpoint GetNegotiateEndpoint(HttpContext context, IEnumerable<ServiceEndpoint> endpoints)
    {
        return endpoints.OrderBy(x => x.EndpointMetrics.ClientConnectionCount).FirstOrDefault(x => x.Online) // Get the available endpoint with minimal clients load
               ?? base.GetNegotiateEndpoint(context, endpoints); // Or fallback to the default behavior to randomly select one from primary endpoints, or fallback to secondary when no primary ones are online
    }
}

Points de terminaison de service de mise à l’échelle dynamique

À partir du kit de développement logiciel (SDK) version 1.5.0, nous allons commencer par activer les points de terminaison de service à l’échelle dynamique pour ASP.NET Core. Vous n’avez donc pas à redémarrer le serveur d’application lorsque vous devez ajouter/supprimer un point de terminaison de service. Comme ASP.NET Core prend en charge la configuration par défaut, comme appsettings.json avec reloadOnChange: true, vous n’avez pas besoin de modifier le code, qui est pris en charge par nature. Et si vous souhaitez ajouter une configuration personnalisée et utiliser un rechargement à chaud, reportez-vous à Configuration dans ASP.NET Core.

Remarque

Compte tenu de l’heure de configuration de la connexion entre le serveur/le service et le client/service peut être différent, afin de ne pas perdre de message pendant le processus de mise à l’échelle, nous avons une période de préproduction qui attend que les connexions de serveur soient prêtes avant d’ouvrir le nouveau ServiceEndpoint aux clients. En règle générale, il faut des secondes et vous serez en mesure de voir un message de journal comme Succeed in adding endpoint: '{endpoint}' celui qui indique la fin du processus.

Dans certaines situations attendues, telles que les problèmes réseau interrégions ou les incohérences de configuration sur différents serveurs d’applications, la période de préproduction peut ne pas se terminer correctement. Dans ces cas, il est recommandé de redémarrer le serveur d’applications lorsque vous trouvez que le processus de mise à l’échelle ne fonctionne pas correctement.

La période d’expiration par défaut de l’échelle est de 5 minutes, et elle peut être personnalisée en modifiant la valeur dans ServiceOptions.ServiceScaleTimeout. Si vous avez beaucoup de serveurs d’application, il est suggéré d’étendre la valeur un peu plus.

Configuration dans les scénarios inter-régions

L’objet ServiceEndpoint a une propriété EndpointType avec la valeur primary ou secondary.

Les points de terminaison principaux sont des points de terminaison préférés pour recevoir le trafic client, car ils ont des connexions réseau plus fiables. Les points de terminaison secondaires disposent de connexions réseau moins fiables et sont utilisés uniquement pour le serveur vers le trafic client. Par exemple, les points de terminaison secondaires sont utilisés pour diffuser des messages au lieu du trafic client vers le serveur.

Dans les cas interrégions, le réseau peut être instable. Pour un serveur d’applications situé dans la région USA Est, le point de terminaison SignalR Service situé dans la même région USA Est est primary et les points de terminaison dans d’autres régions marquées comme secondary. Dans cette configuration, les points de terminaison de service d’autres régions peuvent recevoir des messages de ce serveur d’applications USA Est, mais aucun client interrégion n’est acheminé vers ce serveur d’applications. Le diagramme suivant présente l’architecture :

Cross-Geo Infra

Lorsqu’un client tente /negotiate avec le serveur d’applications avec un routeur par défaut, le SDK sélectionne de façon aléatoire un point de terminaison dans l’ensemble de points de terminaison disponibles primary . Lorsque le point de terminaison principal n’est pas disponible, le Kit de développement logiciel (SDK ) sélectionne de manière aléatoire tous les points de terminaison disponibles secondary . Le point de terminaison est marqué comme disponible quand la connexion entre le serveur et le point de terminaison de service est active.

Dans un scénario interrégion, lorsqu’un client tente /negotiate avec le serveur d’applications hébergé dans la région USA Est, par défaut, il retourne toujours le primary point de terminaison situé dans la même région. Lorsque tous les points de terminaison USA Est ne sont pas disponibles, le routeur redirige le client vers des points de terminaison dans d’autres régions. La section de basculement suivante décrit le scénario en détail.

Normal Negotiate

Type de basculement

Lorsqu’aucun point de terminaison n’est primary/negotiate disponible, le client choisit parmi les points de terminaison disponibles secondary . Ce mécanisme de basculement nécessite que chaque point de terminaison sert de primary point de terminaison à au moins un serveur d’applications.

Diagram showing the Failover mechanism process.

Étapes suivantes

Vous pouvez utiliser plusieurs points de terminaison dans des scénarios de haute disponibilité et de récupération d’urgence.