Hospedagem e colocação em escala do SignalR do ASP.NET Core

Por Andrew Stanton-Nurse, Brady Gaster e Tom Dykstra

Este artigo explica as considerações de hospedagem e colocação em escala para aplicativos de alto tráfego que usam o SignalR do ASP.NET Core.

Sessões autoadesivas

SignalR requer que todas as solicitações HTTP para uma conexão específica sejam tratadas pelo mesmo processo de servidor. Quando SignalR está em execução em um farm de servidores (vários servidores), "sessões autoadesivas" devem ser usadas. "Sessões autoadesivas" também são chamadas de afinidade de sessão por alguns balanceadores de carga. O Serviço de Aplicativo do Azure usa Roteamento de Solicitação de Aplicativo (ARR) para rotear solicitações. Habilitar a configuração "Afinidade ARR" em seu Serviço de Aplicativo do Azure habilitará "sessões autoadesivas". As únicas circunstâncias em que as sessões autoadesivas não são necessárias são:

  1. Ao hospedar em um único servidor, em um único processo.
  2. Ao usar o Serviço Azure SignalR.
  3. Quando todos os clientes são configurados para usar apenas WebSockets, e a configuração SkipNegotiation está habilitada na configuração do cliente.

Em todas as outras circunstâncias (incluindo quando o backplane do Redis é usado), o ambiente do servidor deve ser configurado para sessões autoadesivas.

Para obter diretrizes sobre como configurar o Serviço de Aplicativo do Azure para SignalR, confira Publicar um aplicativo SignalR do ASP.NET Core no Serviço de Aplicativo do Azure. Para obter diretrizes sobre como configurar sessões adesivas para aplicativos Blazor que usam o Serviço SignalR do Azure, consulte Hospedar e implantar Blazor aplicativos do ASP.NET Core no lado do servidor.

Recursos de conexão TCP

O número de conexões TCP simultâneas às quais um servidor Web pode dar suporte é limitado. Os clientes HTTP padrão usam conexões efêmeras. Essas conexões podem ser fechadas quando o cliente fica ocioso e reaberto mais tarde. Por outro lado, uma conexão do SignalR é persistente. As conexões do SignalR permanecem abertas mesmo quando o cliente fica ocioso. Em um aplicativo de alto tráfego que atende a muitos clientes, essas conexões persistentes podem fazer com que os servidores atinjam o número máximo de conexões.

As conexões persistentes também consomem memória adicional para acompanhar cada conexão.

O uso intenso de recursos relacionados à conexão por SignalR pode afetar outros aplicativos Web hospedados no mesmo servidor. Quando SignalR abre e mantém as últimas conexões TCP disponíveis, outros aplicativos Web no mesmo servidor também não têm mais conexões disponíveis.

Se um servidor ficar sem conexões, você verá erros aleatórios de soquete e erros de redefinição de conexão. Por exemplo:

An attempt was made to access a socket in a way forbidden by its access permissions...

Para impedir que o uso de recursos pelo SignalR cause erros em outros aplicativos Web, execute o SignalR em servidores diferentes dos outros aplicativos Web.

Para impedir que o uso de recursos pelo SignalR cause erros em um aplicativo do SignalR, escale horizontalmente para limitar o número de conexões com as quais um servidor precisa lidar.

Escalar horizontalmente

Um aplicativo que usa o SignalR precisa acompanhar todas as suas conexões, o que cria problemas para um farm de servidores. Adicione um servidor e ele obtém novas conexões que os outros servidores não conhecem. Por exemplo, o SignalR em cada servidor no diagrama a seguir não está ciente das conexões nos outros servidores. Quando SignalR em um dos servidores deseja enviar uma mensagem para todos os clientes, a mensagem só é enviada para os clientes conectados a esse servidor.

Scaling SignalR without a backplane

As opções para resolver esse problema são Serviço Azure SignalR e o backplane do Redis.

Serviço SignalR do Azure

O Serviço Azure SignalR funciona como um proxy para tráfego em tempo real e funciona como um backplane quando o aplicativo é escalado horizontalmente em vários servidores. Sempre que um cliente inicia uma conexão com o servidor, o cliente é redirecionado para se conectar ao serviço. O processo é ilustrado pelo seguinte diagrama:

Establishing a connection to the Azure SignalR Service

O resultado é que o serviço gerencia todas as conexões de cliente, enquanto cada servidor precisa apenas de um pequeno número constante de conexões com o serviço, conforme mostrado no diagrama a seguir:

Clients connected to the service, servers connected to the service

Essa abordagem para expansão tem várias vantagens em relação à alternativa de backplane do Redis:

  • As sessões autoadesivas, também conhecidas como afinidade de cliente, não são necessárias, pois os clientes são redirecionados imediatamente para o Serviço Azure SignalR quando se conectam.
  • Um aplicativo do SignalR pode escalar horizontalmente com base no número de mensagens enviadas, enquanto o Serviço Azure SignalR é dimensionado para lidar com qualquer número de conexões. Por exemplo, pode haver milhares de clientes, mas se apenas algumas mensagens por segundo forem enviadas, o aplicativo SignalR não precisará escalar horizontalmente para vários servidores apenas para lidar com as próprias conexões.
  • Um aplicativo do SignalR não usará significativamente mais recursos de conexão do que um aplicativo Web sem o SignalR.

Por esses motivos, recomendamos o Serviço Azure SignalR para todos os aplicativos SignalR do ASP.NET Core hospedados no Azure, incluindo Serviço de Aplicativo, VMs e contêineres.

Para obter mais informações, confira a documentação do Serviço Azure SignalR.

Backplane de Redis

O Redis é um repositório de chave-valor na memória que dá suporte a um sistema de mensagens com um modelo de publicação/assinatura. O backplane do Redis do SignalR usa o recurso pub/sub para encaminhar mensagens para outros servidores. Quando um cliente faz uma conexão, as informações de conexão são passadas para o backplane. Quando um servidor deseja enviar uma mensagem para todos os clientes, ele envia para o backplane. O backplane conhece todos os clientes conectados e em quais servidores eles estão. Ele envia a mensagem a todos os clientes por meio de seus respectivos servidores. Esse processo é ilustrado no diagrama a seguir:

Redis backplane, message sent from one server to all clients

O backplane do Redis é a abordagem de expansão recomendada para aplicativos hospedados em sua própria infraestrutura. Se houver uma latência de conexão significativa entre o seu data center e um data center do Azure, o Serviço Azure SignalR poderá não ser uma opção prática para aplicativos locais com baixa latência ou requisitos de alta taxa de transferência.

As vantagens do Serviço Azure SignalR observadas anteriormente são desvantagens para o backplane do Redis:

  • As sessões autoadesivas, também conhecidas como afinidade de cliente, são necessárias, exceto quando ambos os seguintes itens são verdadeiros:
    • Todos os clientes são configurados para usar somente WebSockets.
    • A configuração SkipNegotiation está habilitada na configuração do cliente. Depois que uma conexão é iniciada em um servidor, a conexão deve permanecer nesse servidor.
  • Um aplicativo do SignalR deve ser escalado horizontalmente com base no número de clientes, mesmo que poucas mensagens estejam sendo enviadas.
  • Um aplicativo SignalR usa significativamente mais recursos de conexão do que um aplicativo Web sem SignalR.

Limitações do IIS no sistema operacional cliente Windows

Windows 10 e Windows 8.x são sistemas operacionais cliente. O IIS em sistemas operacionais cliente tem um limite de 10 conexões simultâneas. As conexões de SignalR são:

  • Transitório e frequentemente restabelecida.
  • Não descartadas imediatamente quando não são mais usadas.

As condições anteriores tornam provável que ele atinja o limite de 10 conexões em um sistema operacional cliente. Quando um sistema operacional cliente é usado para desenvolvimento, recomendamos:

  • Evite o IIS.
  • Use Kestrel ou IIS Express como destinos de implantação.

Linux com o Nginx

O seguinte contém as configurações mínimas necessárias para habilitar WebSockets, ServerSentEvents e LongPolling para o SignalR:

http {
  map $http_connection $connection_upgrade {
    "~*Upgrade" $http_connection;
    default keep-alive;
  }

  server {
    listen 80;
    server_name example.com *.example.com;

    # Configure the SignalR Endpoint
    location /hubroute {
      # App server url
      proxy_pass http://localhost:5000;

      # Configuration for WebSockets
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_cache off;
      # WebSockets were implemented after http/1.0
      proxy_http_version 1.1;

      # Configuration for ServerSentEvents
      proxy_buffering off;

      # Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds
      proxy_read_timeout 100s;

      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
    }
  }
}

Quando vários servidores de back-end são usados, sessões autoadesivas devem ser adicionadas para impedir que as conexões do SignalR alternem servidores ao se conectarem. Há várias maneiras de adicionar sessões autoadesivas no Nginx. Duas abordagens são mostradas abaixo, dependendo do que você tem disponível.

O exemplo a seguir é adicionado além da configuração anterior. Nos exemplos a seguir, backend é o nome do grupo de servidores.

Com o Nginx Open Source, use ip_hash para rotear conexões para um servidor com base no endereço IP do cliente:

http {
  upstream backend {
    # App server 1
    server localhost:5000;
    # App server 2
    server localhost:5002;

    ip_hash;
  }
}

Com o Nginx Plus, use sticky para adicionar um cookie às solicitações e fixar as solicitações do usuário em um servidor:

http {
  upstream backend {
    # App server 1
    server localhost:5000;
    # App server 2
    server localhost:5002;

    sticky cookie srv_id expires=max domain=.example.com path=/ httponly;
  }
}

Por fim, altere proxy_pass http://localhost:5000 na seção server para proxy_pass http://backend.

Para obter mais informações relacionadas a WebSockets sobre o Nginx, confira NGINX como um proxy WebSocket.

Para obter mais informações sobre balanceamento de carga e sessões autoadesivas, confira Balanceamento de carga do NGINX.

Para obter mais informações sobre o ASP.NET Core com Nginx, confira o seguinte artigo:

Provedores de backplane de terceiros do SignalR

Próximas etapas

Para saber mais, consulte os recursos a seguir: