Problemas de desempenho ao fazer chamadas para serviços Web de um aplicativo ASP.NET

Este artigo fornece ajuda para resolver os problemas de desempenho que ocorrem quando você faz chamadas para serviços Web de um aplicativo microsoft ASP.NET.

Versão original do produto: ASP.NET
Número de KB original: 821268

Sintomas

Ao fazer chamadas para serviços Web de um aplicativo ASP.NET, você pode ter contenção, desempenho ruim e impasses. Os clientes podem relatar que as solicitações param de responder ou levam muito tempo para serem executadas. Se houver suspeita de um impasse, o processo de trabalho poderá ser reciclado.

Você pode receber a seguinte mensagem de erro de exceção ao fazer uma chamada para o HttpWebRequest.GetResponse método:

"System.InvalidOperationException: não havia threads gratuitos suficientes no objeto ThreadPool para concluir a operação."

Você também pode receber a seguinte mensagem de erro de exceção no navegador:

"HttpException (0x80004005): Tempo limite de solicitação."

Observação

Este artigo também se aplica a aplicativos que fazem HttpWebRequest solicitações diretamente.

Motivo

Esse problema pode ocorrer porque ASP.NET limita o número de threads de trabalho e threads de porta de conclusão que uma chamada pode usar para executar solicitações.

Normalmente, uma chamada para um serviço Web usa um thread de trabalho para executar o código que envia a solicitação e um thread de porta de conclusão para receber o retorno de chamada do serviço Web. No entanto, se a solicitação for redirecionada ou exigir autenticação, a chamada poderá usar até dois threads de trabalho e dois threads de porta de conclusão. Portanto, você pode esgotar o gerenciado ThreadPool quando várias chamadas de serviço Web ocorrem ao mesmo tempo.

Por exemplo, suponha que o ThreadPool esteja limitado a 10 threads de trabalho e que todos os 10 threads de trabalho estejam executando o código que está aguardando um retorno de chamada para ser executado. O retorno de chamada nunca pode ser executado, pois todos os itens de trabalho que estão enfileirados no ThreadPool estão bloqueados até que um thread fique disponível.

Outra fonte potencial de contenção é o maxconnection parâmetro que o System.Net namespace usa para limitar o número de conexões. Geralmente, esse limite funciona conforme o esperado. No entanto, se muitos aplicativos tentarem fazer muitas solicitações para um único endereço IP ao mesmo tempo, os threads poderão ter que esperar por uma conexão disponível.

Resolução

Para resolve esses problemas, você pode ajustar os seguintes parâmetros no arquivo Machine.config para se adequar melhor à sua situação:

  • maxWorkerThreads
  • minWorkerThreads
  • maxIoThreads
  • minFreeThreads
  • minLocalRequestFreeThreads
  • maxconnection
  • executionTimeout

Para resolve esses problemas com êxito, execute as seguintes ações:

  • Limite o número de solicitações ASP.NET que podem ser executadas ao mesmo tempo para aproximadamente 12 por CPU.
  • Permitir que os retornos de chamada do serviço Web usem threads livremente no ThreadPool.
  • Selecione um valor apropriado para o maxconnections parâmetro. Baseie sua seleção no número de endereços IP e AppDomains usados.

Observação

A recomendação de limitar o número de solicitações de ASP.NET para 12 por CPU é um pouco arbitrária. No entanto, esse limite provou funcionar bem para a maioria dos aplicativos.

MaxWorkerThreads e maxIoThreads

ASP.NET usa as duas seguintes configurações de configuração para limitar o número máximo de threads de trabalho e threads de conclusão usados:

<processModel maxWorkerThreads="20" maxIoThreads="20">

O maxWorkerThreads parâmetro e o maxIoThreads parâmetro são implicitamente multiplicados pelo número de CPUs. Por exemplo, se você tiver dois processadores, o número máximo de threads de trabalho será 2 * maxWorkerThreads.

MinFreeThreads e minLocalRequestFreeThreads

ASP.NET também contém as seguintes configurações de configuração que determinam quantos threads de trabalho e threads de porta de conclusão devem estar disponíveis para iniciar uma solicitação remota ou uma solicitação local:

<httpRuntime minFreeThreads="8" minLocalRequestFreeThreads="8">

Se não houver threads suficientes disponíveis, a solicitação será enfileirada até que threads suficientes sejam gratuitos para fazer a solicitação. Portanto, ASP.NET não executará mais do que o seguinte número de solicitações ao mesmo tempo:

(maxWorkerThreads * número de CPUs) - minFreeThreads

Observação

O minFreeThreads parâmetro e o minLocalRequestFreeThreads parâmetro não são implicitamente multiplicados pelo número de CPUs.

Minworkerthreads

ASP.NET também contém a seguinte configuração que determina quantos threads de trabalho podem ser disponibilizados imediatamente para atender a uma solicitação remota.

<processModel minWorkerThreads="1">

Os threads controlados por essa configuração podem ser criados a uma taxa muito mais rápida do que os threads de trabalho criados a partir dos recursos padrão de ajuste de thread do CLR (Common Language Runtime).

Essa configuração permite ASP.NET a solicitações de serviço que podem estar preenchendo repentinamente a fila de solicitação ASP.NET devido a uma lentidão em um servidor de back-end, uma explosão repentina de solicitações do final do cliente ou algo semelhante que causaria um aumento repentino no número de solicitações na fila.

O valor padrão do minWorkerThreads parâmetro é 1. Recomendamos que você defina o valor do minWorkerThreads parâmetro como o seguinte valor:

minWorkerThreads = maxWorkerThreads / 2

Por padrão, o minWorkerThreads parâmetro não está presente no arquivo Web.config nem no arquivo Machine.config . Essa configuração é implicitamente multiplicada pelo número de CPUs.

Maxconnection

O maxconnection parâmetro determina quantas conexões podem ser feitas em um endereço IP específico. O parâmetro aparece da seguinte maneira:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://65.53.32.230" maxconnection="12">
</connectionManagement>

Se o código do aplicativo referenciar o aplicativo por nome de host em vez de endereço IP, o parâmetro deverá aparecer da seguinte maneira:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://hostname" maxconnection="12">
</connectionManagement>

Por fim, se o aplicativo estiver hospedado em uma porta diferente de 80, o parâmetro deverá incluir a porta não padrão na URL, semelhante à seguinte:

<connectionManagement>
    <add address="*" maxconnection="2">
    <add address="http://hostname:8080" maxconnection="12">
</connectionManagement>

As configurações para os parâmetros que são discutidos anteriormente neste artigo estão todas no nível do processo. No entanto, a configuração de maxconnection parâmetro se aplica ao nível appDomain. Por padrão, como essa configuração se aplica ao nível appDomain, você pode criar um máximo de duas conexões com um endereço IP específico de cada AppDomain em seu processo.

Executiontimeout

ASP.NET usa a seguinte configuração para limitar o tempo de execução da solicitação:

<httpRuntime executionTimeout="90"/>

Você também pode definir esse limite usando a Server.ScriptTimeout propriedade.

Observação

Se você aumentar o valor do executionTimeout parâmetro, também poderá ter que modificar a configuração do processModelresponseDeadlockInterval parâmetro.

Recomendações

As configurações recomendadas nesta seção podem não funcionar para todos os aplicativos. No entanto, as seguintes informações adicionais podem ajudá-lo a fazer os ajustes apropriados.

Se você estiver fazendo uma chamada de serviço Web para um único endereço IP de cada página ASPX, a Microsoft recomenda que você use as seguintes configurações:

  • Defina os valores do maxWorkerThreads parâmetro e do maxIoThreads parâmetro como 100.
  • Defina o valor do maxconnection parâmetro como 12*N (em que N é o número de CPUs que você tem).
  • Defina os valores do minFreeThreads parâmetro como 88*N e o minLocalRequestFreeThreads parâmetro como 76*N.
  • Defina o valor de minWorkerThreadscomo 50. Lembre-se de que minWorkerThreads não está no arquivo de configuração por padrão. Você deve adicioná-lo.

Algumas dessas recomendações envolvem uma fórmula simples que envolve o número de CPUs em um servidor. A variável que representa o número de CPUs nas fórmulas é N.

Para essas configurações, se você tiver a hiperthreading habilitada, deverá usar o número de CPUs lógicas em vez do número de CPUs físicas. Por exemplo, se você tiver um servidor de quatro processadores com hiperthreading habilitado, o valor de N nas fórmulas será 8 em vez de 4.

Observação

Ao usar essa configuração, você pode executar no máximo 12 ASP.NET solicitações por CPU ao mesmo tempo porque 100-88=12. Portanto, pelo menos 88*N threads de porta de conclusão e 88*N estão disponíveis para outros usos (como para os retornos de chamada do serviço Web).

Por exemplo, você tem um servidor com quatro processadores e hiperthreading habilitados. Com base nessas fórmulas, você usaria os valores a seguir para as configurações mencionadas neste artigo.

<system.web>
    <processModel maxWorkerThreads="100" maxIoThreads="100" minWorkerThreads="50"/>
    <httpRuntime minFreeThreads="704" minLocalRequestFreeThreads="608"/>
</system.web>
<system.net>
    <connectionManagement>
        <add address="[ProvideIPHere]" maxconnection="96"/>
    </connectionManagement>
</system.net>

Além disso, quando você usa essa configuração, 12 conexões estão disponíveis por CPU por endereço IP para cada AppDomain. Portanto, no cenário a seguir, muito pouca contenção ocorre quando as solicitações estão aguardando conexões, e a ThreadPool não está esgotada:

  • A Web hospeda apenas um aplicativo (AppDomain).
  • Cada solicitação de uma página ASPX faz uma solicitação de serviço Web.
  • Todas as solicitações são para o mesmo endereço IP.

No entanto, quando você usa essa configuração, os cenários que envolvem um dos abaixo provavelmente usarão muitas conexões:

  • As solicitações são para vários endereços IP.
  • As solicitações são redirecionadas (302 status código).
  • As solicitações exigem autenticação.
  • As solicitações são feitas de vários AppDomains.

Nesses cenários, é uma boa ideia usar um valor menor para o maxconnection parâmetro e valores mais altos para o minFreeThreads parâmetro e o minLocalRequestFreeThreads parâmetro.

Mais informações

Para obter mais informações, confira Melhorando ASP.NET desempenho.

Se você estiver enfrentando um fraco desempenho e contenção no IIS junto com ASP.NET, acesse os seguintes blogs da Microsoft: