Temos uma API implementada usando o ServiceStack, hospedado no IIS. Ao realizar o teste de carga da API, descobrimos que os tempos de resposta são bons, mas que se deterioram rapidamente assim que atingimos cerca de 3.500 usuários simultâneos por servidor. Temos dois servidores e, quando atingimos 7.000 usuários, o tempo médio de resposta fica abaixo de 500 ms para todos os pontos de extremidade. Como as caixas estão atrás de um balanceador de carga, temos 3.500 concorrentes por servidor. No entanto, assim que aumentamos o número total de usuários simultâneos, vemos um aumento significativo nos tempos de resposta. Aumentar os usuários simultâneos para 5.000 por servidor fornece um tempo médio de resposta por terminal de cerca de 7 segundos.
A memória e a CPU nos servidores são bastante baixas, enquanto os tempos de resposta são bons e quando eles se deterioram. No pico de 10.000 usuários simultâneos, a CPU fica em média abaixo de 50% e a RAM fica em torno de 3-4 GB em 16. Isso nos deixa pensando que estamos atingindo algum tipo de limite em algum lugar. A captura de tela abaixo mostra alguns contadores importantes no perfmon durante um teste de carga com um total de 10.000 usuários simultâneos. O contador destacado é solicitações / segundo. À direita da captura de tela, você pode ver as solicitações por segundo gráfico se tornando realmente irregulares. Este é o principal indicador para tempos de resposta lentos. Assim que vemos esse padrão, observamos tempos de resposta lentos no teste de carga.
Como resolvemos esse problema de desempenho? Estamos tentando identificar se esse é um problema de codificação ou de configuração. Existem configurações no web.config ou no IIS que possam explicar esse comportamento? O pool de aplicativos está executando o .NET v4.0 e a versão do IIS é 7.5. A única alteração que fizemos das configurações padrão é atualizar o valor de Comprimento da fila do pool de aplicativos de 1.000 para 5.000. Também adicionamos as seguintes configurações ao arquivo Aspnet.config:
<system.web>
<applicationPool
maxConcurrentRequestsPerCPU="5000"
maxConcurrentThreadsPerCPU="0"
requestQueueLimit="5000" />
</system.web>
Mais detalhes:
O objetivo da API é combinar dados de várias fontes externas e retornar como JSON. Atualmente, ele está usando uma implementação de cache do InMemory para armazenar em cache chamadas externas individuais na camada de dados. A primeira solicitação para um recurso buscará todos os dados necessários e quaisquer solicitações subsequentes para o mesmo recurso obterão resultados do cache. Temos um 'corredor de cache' que é implementado como um processo em segundo plano que atualiza as informações no cache em determinados intervalos definidos. Adicionamos bloqueio ao código que busca dados dos recursos externos. Também implementamos os serviços para buscar os dados das fontes externas de forma assíncrona, de modo que o ponto de extremidade seja tão lento quanto a chamada externa mais lenta (a menos que tenhamos dados no cache, é claro). Isso é feito usando a classe System.Threading.Tasks.Task.Poderíamos estar atingindo uma limitação em termos de número de threads disponíveis para o processo?