Hospedagem e escalabilidade do ASP.NET Core SignalR

Por Ashley Stanton-Nurse, Brady Gaster e Tom Dykstra

Este artigo explica as considerações sobre hospedagem e escalabilidade para aplicativos de alto tráfego que usam o ASP.NET Core SignalR.

Sessões persistentes

SignalR requer o mesmo processo de servidor para lidar com todas as solicitações HTTP para uma conexão específica. Quando SignalR é executado em um farm de servidores (vários servidores), "sessões persistentes" devem ser usadas. "Sessões persistentes" também são chamadas de afinidade de sessão. Serviço de Aplicativo do Azure usa Microsoft ARR (Application Request Routing) para rotear solicitações. Habilitar a configuração "Afinidade de sessão" (Afinidade ARR) no seu aplicativo do App Service habilita sessões persistentes.

Há três cenários em que sessões persistentes não são necessárias para um aplicativo:

  • Hospedagem em um único servidor em um único processo
  • Usando o serviço Azure SignalR (as sessões persistentes estão habilitadas para o serviço, não para o aplicativo)
  • Todos os clientes são configurados para usar somente WebSockets e a configuração do cliente habilita SkipNegotiation

Em todos os outros cenários (incluindo quando o backplane Redis é usado), o ambiente do servidor deve ser configurado para sessões persistentes.

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 está inativo e reabertas mais tarde. Por outro lado, uma SignalR conexão é persistente. SignalR conexões permanecem abertas mesmo quando o cliente fica inativo. 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 extra, 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 SignalR precisa manter o controle de todas as suas conexões, o que cria problemas para um conjunto 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.

Ilustração que ilustra o dimensionamento SignalR sem um plano de fundo.

As opções para resolver esse problema são o Serviço do 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 diagrama seguinte ilustra este processo:

Ilustração que mostra o estabelecimento de uma conexão com o Azure SignalR Serviço.

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:

Ilustração que ilustra clientes e servidores conectados ao serviço.

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

  • As sessões persistentes, também conhecidas como afinidade de cliente, não são necessárias porque os clientes são redirecionados imediatamente para o Serviço Azure SignalR quando se conectam.
  • Um aplicativo SignalR pode escalar com base no número de mensagens enviadas, enquanto o serviço Azure SignalR é dimensionado para dar suporte a 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 SignalR aplicativo não usa muito mais recursos de conexão do que um aplicativo Web sem SignalR.

Por esses motivos, a recomendação é usar o serviço Azure SignalR para todos os aplicativos ASP.NET Core SignalR hospedados em Azure, incluindo o Serviço de Aplicativo, máquinas virtuais e contêineres.

Para obter mais informações, consulte a documentação Azure SignalR Service.

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 SignalR backplane do Redis usa o recurso de publicação/assinatura para encaminhar mensagens para outros servidores. Quando um cliente estabelece uma conexão, as informações da conexão são encaminhadas ao backplane. Quando um servidor deseja enviar uma mensagem a todos os clientes, ele a 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:

Ilustração que mostra o backplane do Redis, com uma mensagem enviada de um servidor para todos os clientes.

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 data center e um data center Azure, Azure SignalR Service pode 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 descritas anteriormente são desvantagens para o backplane do Redis:

  • Sessões persistentes, também conhecidas como afinidade do cliente, são necessárias, exceto quando ambas as condições a seguir forem verdadeiras:
    • 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 SignalR aplicativo deve ser expandido com base no número de clientes, mesmo ao enviar poucas mensagens.
  • Um SignalR aplicativo usa muito mais recursos de conexão do que um aplicativo Web sem SignalR.

Limitações do IIS em Windows sistema operacional cliente

Windows 10 e Windows 8.x são sistemas operacionais cliente. Serviços de Informações da Internet (IIS) em sistemas operacionais cliente tem um limite de 10 conexões simultâneas. As SignalR conexões têm as seguintes características:

  • Eles são temporários e são restabelecidos com frequência.
  • Eles não são descartados imediatamente quando não são mais usados.

Essas características permitem atingir o limite de 10 conexões em um sistema operacional cliente. Ao usar um sistema operacional cliente para desenvolvimento, considere as seguintes recomendações:

  • Evitar o IIS
  • Usar Kestrel ou IIS Express como destinos de implantação

Linux com o Nginx

O código a seguir contém as configurações mínimas necessárias para habilitar WebSockets, ServerSentEvents e LongPolling para 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 persistentes devem ser adicionadas para impedir que as conexões de SignalR mudem de servidor ao serem estabelecidas. Há várias maneiras de adicionar sessões persistentes no Nginx. Os exemplos a seguir mostram duas abordagens com base no que você tem disponível.

O código a seguir complementa a configuração de exemplo anterior. Nos snippets, 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 cookie às solicitações e fixar as solicitações dos usuários 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;
       }
    }
    
  • Para ambas as configurações, altere proxy_pass http://localhost:5000 na server seção para proxy_pass http://backend.

Você pode encontrar mais informações no Host ASP.NET Core no Linux com nginx.

Outros fornecedores de backplane de SignalR

Os seguintes provedores que não são da Microsoft também oferecem backplane de SignalR: