Escalado de SignalR Service con varias instancias

El SDK de SignalR Service admite varios puntos de conexión para las instancias de SignalR Service. Puede usar esta característica para escalar las conexiones simultáneas, o usarla para la mensajería entre regiones.

Para ASP.NET Core

Adición de varios puntos de conexión desde la configuración

Configure con clave Azure:SignalR:ConnectionString o Azure:SignalR:ConnectionString: para signalR Service cadena de conexión.

Si la clave comienza por Azure:SignalR:ConnectionString:, debe estar en el formato Azure:SignalR:ConnectionString:{Name}:{EndpointType}, donde Name y EndpointType son propiedades del ServiceEndpoint objeto y son accesibles desde el código.

Puede agregar varias cadenas de conexión de instancias con los siguientes comandos dotnet:

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>

Adición de varios puntos de conexión desde el código

Una ServicEndpoint clase describe las propiedades de un punto de conexión de Azure SignalR Service. Puede configurar varios puntos de conexión de instancia al usar el SDK de Azure SignalR Service mediante:

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"),
            };
        });

Personalización del enrutador de punto de conexión

De forma predeterminada, el SDK usa DefaultEndpointRouter para seleccionar los puntos de conexión.

Comportamiento predeterminado

  1. Enrutamiento de solicitudes de cliente:

    Cuando el cliente negocia (acción /negotiate) con el servidor de aplicaciones. De forma predeterminada, el SDK selecciona aleatoriamente un punto de conexión del conjunto de puntos de conexión de servicio disponibles.

  2. Enrutamiento de mensajes del servidor:

    Al enviar un mensaje a una conexión específica y la conexión de destino se enruta al servidor actual, el mensaje pasa directamente a ese punto de conexión conectado. En caso contrario, los mensajes se difunden a cada punto de conexión de Azure SignalR.

Personalización del algoritmo de enrutamiento

Puede crear su propio enrutador cuando tenga conocimientos especiales para identificar los puntos de conexión a los que deberían ir los mensajes.

En el ejemplo siguiente se define un enrutador personalizado que enruta los mensajes con un grupo a partir east- del punto de conexión denominado 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);
    }
}

En el ejemplo siguiente se invalida el comportamiento de negociación predeterminado y se selecciona el punto de conexión en función de la ubicación del servidor de aplicaciones.

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

No olvide registrar el enrutador en el contenedor de DI mediante:

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

Para ASP.NET

Adición de varios puntos de conexión desde la configuración

Configuración con clave Azure:SignalR:ConnectionString o Azure:SignalR:ConnectionString: para signalR Service cadena de conexión.

Si la clave empieza por Azure:SignalR:ConnectionString:, debe estar en formato Azure:SignalR:ConnectionString:{Name}:{EndpointType}, donde Name y EndpointType son propiedades del objeto ServiceEndpoint y son accesibles desde el código.

Puede agregar varias cadenas de conexión de instancia a 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>

Adición de varios puntos de conexión desde el código

Una ServiceEndpoint clase describe las propiedades de un punto de conexión de Azure SignalR Service. Puede configurar varios puntos de conexión de instancia al usar el SDK de Azure SignalR Service mediante:

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>"),
            }
        });

Personalización de un enrutador

La única diferencia entre ASP.NET SignalR y ASP.NET Core SignalR es el tipo de contexto http para GetNegotiateEndpoint. En ASP.NET SignalR, es de tipo IOwinContext.

El código siguiente es un ejemplo de negociación personalizado para 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
    }
}

No olvide registrar el enrutador en el contenedor de DI mediante:

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étricas de punto de conexión de servicio

Para habilitar un enrutador avanzado, el SDK del servidor signalR proporciona varias métricas para ayudar al servidor a tomar decisiones inteligentes. Las propiedades están en ServiceEndpoint.EndpointMetrics.

Nombre de la métrica Descripción
ClientConnectionCount Recuento total de conexiones de cliente simultáneas en todos los centros para el punto de conexión de servicio
ServerConnectionCount Recuento total de conexiones de servidor simultáneas en todos los centros para el punto de conexión de servicio
ConnectionCapacity Cuota de conexión total para el punto de conexión de servicio, incluidas las conexiones de cliente y servidor

El código siguiente es un ejemplo de personalización de un enrutador según 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
    }
}

ServiceEndpoints de escalado dinámico

Desde la versión 1.5.0 del SDK, habilitamos ServiceEndpoints de escalado dinámico para la primera versión de ASP.NET Core. Por lo tanto, no tiene que reiniciar el servidor de aplicaciones cuando necesite agregar o quitar un ServiceEndpoint. Puesto que ASP.NET Core admite una configuración predeterminada, como appsettings.json con reloadOnChange: true, no es necesario cambiar ningún código y es compatible por naturaleza. Y si desea agregar alguna configuración personalizada y trabajar con recarga activa, consulte Configuración en ASP.NET Core.

Nota:

Teniendo en cuenta el tiempo de configuración de la conexión entre el servidor, el servicio y el cliente o el servicio pueden ser diferentes, para garantizar que no se pierda ningún mensaje durante el proceso de escalado, tenemos un período de almacenamiento provisional en espera de que las conexiones del servidor estén listas antes de abrir el nuevo ServiceEndpoint a los clientes. Normalmente, tarda segundos en completarse y podrá ver un mensaje de registro como Succeed in adding endpoint: '{endpoint}' el que indica que el proceso se ha completado.

En algunas situaciones esperadas, como problemas de red entre regiones o incoherencias de configuración en distintos servidores de aplicaciones, es posible que el período de ensayo no finalice correctamente. En estos casos, se recomienda reiniciar el servidor de aplicaciones cuando encuentre que el proceso de escalado no funciona correctamente.

El período de tiempo de espera predeterminado para la escala es de 5 minutos y se puede personalizar cambiando el valor de ServiceOptions.ServiceScaleTimeout. Si tiene muchos servidores de aplicaciones, se recomienda ampliar el valor un poco más.

Configuración en escenarios entre regiones

El objeto ServiceEndpoint tiene una propiedad EndpointType con valor primary o secondary.

Los puntos de conexión principales son puntos de conexión preferidos para recibir tráfico de cliente porque tienen conexiones de red más confiables. Los puntos de conexión secundarios tienen conexiones de red menos confiables y solo se usan para el tráfico del servidor al cliente. Por ejemplo, los puntos de conexión secundarios se usan para difundir mensajes en lugar de para el tráfico de cliente al servidor.

En casos entre regiones, la red puede ser inestable. Para un servidor de aplicaciones ubicado en este de EE. UU., el punto de conexión de SignalR Service ubicado en la misma región este de EE. UU . es primary y los puntos de conexión de otras regiones marcadas como secondary. En esta configuración, los puntos de conexión de servicio de otras regiones pueden recibir mensajes de este servidor de aplicaciones de Este de EE. UU ., pero no se enrutan clientes entre regiones a este servidor de aplicaciones. En el siguiente diagrama se muestra la arquitectura:

Cross-Geo Infra

Cuando un cliente intenta /negotiate con el servidor de aplicaciones con un enrutador predeterminado, el SDK selecciona aleatoriamente un punto de conexión del conjunto de puntos de conexión disponibles primary . Cuando el punto de conexión principal no está disponible, el SDK selecciona aleatoriamente de todos los puntos de conexión disponibles secondary . El punto de conexión se marca como disponible cuando la conexión entre el servidor y el punto de conexión de servicio está activa.

En un escenario entre regiones, cuando un cliente intenta /negotiate con el servidor de aplicaciones hospedado en Este de EE. UU., de forma predeterminada siempre devuelve el primary punto de conexión ubicado en la misma región. Cuando todos los puntos de conexión del Este de EE. UU . no están disponibles, el enrutador redirige el cliente a los puntos de conexión de otras regiones. En la siguiente sección de conmutación por error se describe el escenario en detalle.

Normal Negotiate

Conmutación por error

Cuando no haya ningún primary punto de conexión disponible, el cliente /negotiate elige entre los puntos de conexión disponibles secondary . Este mecanismo de conmutación por error requiere que cada punto de conexión actúe como punto primary de conexión para al menos un servidor de aplicaciones.

Diagram showing the Failover mechanism process.

Pasos siguientes

Puede usar varios puntos de conexión en escenarios de alta disponibilidad y recuperación ante desastres.