Partager via


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

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

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 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 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 de serveur :

    Quand un message est envoyé à une connexion spécifique et que la connexion cible est acheminée vers le serveur actuel, le message parvient directement à ce point de terminaison connectée. 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- vers 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 la chaîne de connexion de SignalR Service.

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

Étant donné que le temps d’établissement de la connexion entre le serveur/service et le client/service peut être différent, pour garantir qu’il n’y a pas de perte de messages pendant le processus de mise à l’échelle, nous avons une période d’attente pour que les connexions du serveur soient prêtes avant d’ouvrir le nouveau point de terminaison de service aux clients. En règle générale, l’exécution du processus prend quelques secondes et vous serez en mesure de voir un message de journal comme Succeed in adding endpoint: '{endpoint}', qui indique que le processus est terminé.

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 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 dans les autres régions peuvent recevoir les messages de ce serveur d’applications USA Est, mais aucun client inter-régions n’est routé vers ce serveur d’applications. Le diagramme suivant présente l’architecture :

Infrastructure multigéographique

Quand un client tente de négocier (/negotiate) avec le serveur d’applications, avec un routeur par défaut, le SDK sélectionne de manière aléatoire un point de terminaison dans l’ensemble de points de terminaison primary disponibles. Si le point de terminaison principal n’est pas disponible, le SDK effectue alors une sélection aléatoire parmi tous les points de terminaison secondary disponibles. 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 multirégion, quand un client tente de négocier (/negotiate) avec le serveur d’applications hébergé dans la région USA Est, par défaut, il retourne toujours le point de terminaison primary 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.

Négociation normale

Basculement

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

Diagramme montrant le processus du mécanisme de Basculement.

Étapes suivantes

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