Dimensione o serviço SignalR com várias instâncias

O SDK do Serviço do SignalR dá suporte a vários pontos de extremidade para instâncias de Serviço do SignalR. Você pode usar esse recurso para escalar as conexões simultâneas ou para mensagens entre regiões.

Para ASP.NET Core

Adicionar vários pontos de extremidade da configuração

Configure com chave Azure:SignalR:ConnectionString ou Azure:SignalR:ConnectionString: para a cadeia de conexão do Serviço SignalR.

Se a chave começar com Azure:SignalR:ConnectionString:, ela deverá estar no formato Azure:SignalR:ConnectionString:{Name}:{EndpointType}, onde Name e são propriedades do objeto e EndpointType são acessíveis a ServiceEndpoint partir do código.

Você pode adicionar várias cadeias de conexão de instância usando os seguintes 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>

Adicionar vários pontos de extremidade a partir do código

Uma ServicEndpoint classe descreve as propriedades de um ponto de extremidade do Serviço SignalR do Azure. Você pode configurar vários pontos de extremidade de instância usando o SDK do Serviço do Azure SignalR por meio de:

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

Personalizar o roteador de ponto de extremidade

Por padrão, o SDK usa o DefaultEndpointRouter para escolher os pontos de extremidade.

Comportamento padrão

  1. Roteamento de solicitações do cliente:

    Quando o cliente /negotiate com o servidor de aplicativos. Por padrão, o SDK seleciona aleatoriamente um ponto de extremidade do conjunto de pontos de extremidade de serviço disponíveis.

  2. Roteamento de mensagens do servidor:

    Ao enviar uma mensagem para uma conexão específica e a conexão de destino é roteada para o servidor atual, a mensagem vai diretamente para esse ponto de extremidade conectado. Caso contrário, as mensagens são transmitidas para todos os pontos de extremidade do Azure SignalR.

Personalizar o algoritmo de roteamento

Você pode criar seu próprio roteador se você tem um conhecimento especial para identificar para quais pontos de extremidade as mensagens devem ir.

O exemplo a seguir define um roteador personalizado que roteia mensagens com um grupo começando com east- para o ponto de extremidade chamado 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);
    }
}

O exemplo a seguir substitui o comportamento de negociação padrão e seleciona o ponto de extremidade dependendo do local do servidor do aplicativo.

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ão se esqueça de registrar o roteador para o contêiner DI usando:

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

Adicionar vários pontos de extremidade da configuração

Configuração com chave Azure:SignalR:ConnectionString ou Azure:SignalR:ConnectionString: para cadeia de conexão do Serviço SignalR.

Se a chave começar com Azure:SignalR:ConnectionString:, ela deverá estar no formato Azure:SignalR:ConnectionString:{Name}:{EndpointType}, em que Name e EndpointType são propriedades do objeto ServiceEndpoint e poderão ser acessadas do código.

Você pode adicionar várias cadeias de conexão de instância 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>

Adicionar vários pontos de extremidade a partir do código

Uma ServiceEndpoint classe descreve as propriedades de um ponto de extremidade do Serviço SignalR do Azure. Você pode configurar vários pontos de extremidade de instância usando o SDK do Serviço do Azure SignalR por meio de:

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

Personalizar um roteador

A única diferença entre o SignalR do ASP.NET e o SignalR do ASP.NET Core é o tipo de contexto de http para GetNegotiateEndpoint. Para o SignalR do ASP.NET, o tipo é IOwinContext.

O código a seguir é um exemplo de negociação personalizada 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
    }
}

Não se esqueça de registrar o roteador para o contêiner DI usando:

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 do ponto de extremidade de serviço

Para habilitar um roteador avançado, o SDK do servidor SignalR fornece várias métricas para ajudar o servidor a tomar decisões inteligentes. As propriedades estão em ServiceEndpoint.EndpointMetrics.

Nome da métrica Descrição
ClientConnectionCount Contagem total de conexões de cliente simultâneas em todos os hubs para o ponto de extremidade de serviço
ServerConnectionCount Contagem total de conexões de servidor simultâneas em todos os hubs para o ponto de extremidade de serviço
ConnectionCapacity Cota total de conexão para o ponto de extremidade de serviço, incluindo conexões de cliente e servidor

O código a seguir é um exemplo de personalização de um roteador de ClientConnectionCountacordo com o .

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 Escala Dinâmica

Do SDK versão 1.5.0, estamos habilitando primeiro ServiceEndpoints de escala dinâmica para a versão do ASP.NET Core. Portanto, você não precisa reiniciar o servidor de aplicativos quando precisar adicionar/remover um ServiceEndpoint. Como o ASP.NET Core é compatível com uma configuração padrão como appsettings.json com reloadOnChange: true, você não precisa alterar código e ele tem suporte por natureza. E se você quiser adicionar configurações personalizadas e trabalhar com a recarga dinâmica, confira Configuração no ASP.NET Core.

Observação

Considerando que o tempo de configuração da conexão entre servidor/serviço e cliente/serviço pode ser diferente, para garantir que nenhuma perda de mensagem durante o processo de dimensionamento, temos um período de preparo aguardando que as conexões do servidor estejam prontas antes de abrir o novo ServiceEndpoint para os clientes. Normalmente, leva segundos para ser concluído e você poderá ver uma mensagem de log como Succeed in adding endpoint: '{endpoint}' a que indica o processo concluído.

Em algumas situações esperadas, como problemas de rede entre regiões ou inconsistências de configuração em diferentes servidores de aplicativos, o período de preparo pode não ser concluído corretamente. Nesses casos, sugere-se reiniciar o servidor de aplicativos quando você encontrar o processo de dimensionamento não funcionando corretamente.

O período de tempo limite padrão para a escala é de 5 minutos e pode ser personalizado alterando o valor em ServiceOptions.ServiceScaleTimeout. Se você tiver muitos servidores de aplicativos, sugere-se estender o valor um pouco mais.

Configuração em cenários entre regiões

O objeto ServiceEndpoint tem uma propriedade EndpointType com valor primary ou secondary.

Os pontos de extremidade primários são pontos de extremidade preferenciais para receber tráfego do cliente porque eles têm conexões de rede mais confiáveis. Os pontos de extremidade secundários têm conexões de rede menos confiáveis e são usados apenas para tráfego de servidor para cliente. Por exemplo, pontos de extremidade secundários são usados para transmitir mensagens em vez de tráfego de cliente para servidor.

Em casos entre regiões, a rede pode ser instável. Para um servidor de aplicativos localizado em Leste dos EUA, o ponto de extremidade do Serviço SignalR localizado na mesma região Leste dos EUA é primary e os pontos de extremidade em outras regiões marcados como secondary. Nessa configuração, os pontos de extremidade de serviço em outras regiões podem receber mensagens desse servidor de aplicativos do Leste dos EUA, mas nenhum cliente entre regiões é roteado para esse servidor de aplicativos. O seguinte diagrama mostra a arquitetura:

Cross-Geo Infra

Quando um cliente tenta /negotiate com o servidor de aplicativos com um roteador padrão, o SDK seleciona aleatoriamente um ponto de extremidade do conjunto de pontos de extremidade disponíveis primary . Quando o ponto de extremidade primário não está disponível, o SDK seleciona aleatoriamente de todos os pontos de extremidade disponíveis secondary . O ponto de extremidade é marcado como disponível quando a conexão entre o servidor e o ponto de extremidade de serviço está ativa.

Em um cenário entre regiões, quando um cliente tenta /negotiate com o servidor de aplicativos hospedado no Leste dos EUA, por padrão, ele sempre retorna o primary ponto de extremidade localizado na mesma região. Quando todos os pontos de extremidade Leste dos EUA não estão disponíveis, o roteador redireciona o cliente para pontos de extremidade em outras regiões. A seção de failover a seguir descreve o cenário em detalhes.

Normal Negotiate

Failover

Quando nenhum primary ponto de extremidade está disponível, o cliente escolhe entre os /negotiate pontos de extremidade disponíveis secondary . Esse mecanismo de failover requer que cada ponto de extremidade sirva como um ponto de extremidade para pelo menos um primary servidor de aplicativos.

Diagram showing the Failover mechanism process.

Próximas etapas

Você pode usar vários pontos de extremidade em cenários de alta disponibilidade e recuperação de desastres.