从 ASP.NET 应用程序调用 Web 服务时出现的性能问题

本文帮助解决从 Microsoft ASP.NET 应用程序调用 Web 服务时出现的性能问题。

原始产品版本: ASP.NET
原始 KB 编号: 821268

症状

从 ASP.NET 应用程序调用 Web 服务时,可能会遇到争用、性能不佳和死锁。 客户端可能会报告请求停止响应或需要很长时间才能执行。 如果怀疑存在死锁,可能会回收工作进程。

调用 方法时 HttpWebRequest.GetResponse ,可能会收到以下异常错误消息:

“System.InvalidOperationException:ThreadPool 对象中没有足够的可用线程来完成操作。”

在浏览器中,还可能会收到以下异常错误消息:

“HttpException (0x80004005) :请求超时。”

注意

本文也适用于直接发出 HttpWebRequest 请求的应用程序。

原因

出现此问题的原因可能是 ASP.NET 限制调用可用于执行请求的工作线程和完成端口线程数。

通常,对 Web 服务的调用使用一个工作线程来执行发送请求的代码和一个完成端口线程来接收来自 Web 服务的回调。 但是,如果请求被重定向或需要身份验证,则调用可能使用多达两个工作线程和两个完成端口线程。 因此,当多个 Web 服务调用同时发生时,可以耗尽托管 ThreadPool 的 。

例如,假设 ThreadPool 限制为 10 个工作线程,并且所有 10 个工作线程当前都在执行等待回调执行的代码。 回调永远无法执行,因为排队到 ThreadPool 的任何工作项在线程可用之前会被阻止。

另一个潜在的争用源是maxconnectionSystem.Net命名空间用于限制连接数的参数。 通常,此限制按预期工作。 但是,如果许多应用程序尝试同时向单个 IP 地址发出多个请求,则线程可能需要等待可用的连接。

解决方案

若要解决这些问题,可以在 Machine.config 文件中优化以下参数,以最大程度地适应你的情况:

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

若要成功解决这些问题,请执行下列操作:

  • 将可以同时执行的 ASP.NET 请求数限制为每个 CPU 大约 12 个。
  • 允许 Web 服务回调自由使用 中的 ThreadPool线程。
  • maxconnections 参数选择适当的值。 根据使用的 IP 地址和 AppDomain 数进行选择。

注意

将 ASP.NET 请求数限制为每个 CPU 12 的建议有点任意。 但是,此限制已证明适用于大多数应用程序。

MaxWorkerThreads 和 maxIoThreads

ASP.NET 使用以下两个配置设置来限制使用的最大工作线程数和完成线程数:

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

参数 maxWorkerThreadsmaxIoThreads 参数隐式乘以 CPU 数。 例如,如果有两个处理器,则最大工作线程数为 2 * maxWorkerThreads

MinFreeThreads 和 minLocalRequestFreeThreads

ASP.NET 还包含以下配置设置,用于确定必须有多少工作线程和完成端口线程才能启动远程请求或本地请求:

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

如果没有足够的可用线程,请求将排队,直到有足够的线程可用发出请求。 因此,ASP.NET 不会同时执行超过以下数量的请求:

maxWorkerThreads (*) CPU 数 -minFreeThreads

注意

参数 minFreeThreadsminLocalRequestFreeThreads 参数不隐式乘以 CPU 数。

MinWorkerThreads

ASP.NET 还包含以下配置设置,用于确定可以立即为远程请求提供服务的工作线程数。

<processModel minWorkerThreads="1">

创建此设置控制的线程的速度比从公共语言运行时 (CLR) 的默认线程优化功能创建的工作线程的速度要快得多。

此设置使 ASP.NET 能够处理可能由于后端服务器速度变慢、客户端请求突然激增或导致队列中请求数突然增加而突然填满 ASP.NET 请求队列的请求。

参数的 minWorkerThreads 默认值为 1。 建议将 参数的值 minWorkerThreads 设置为以下值:

minWorkerThreads = maxWorkerThreads / 2

默认情况下, minWorkerThreadsWeb.config 文件或 Machine.config 文件中不存在参数。 此设置隐式乘以 CPU 数。

Maxconnection

参数 maxconnection 确定可以与特定 IP 地址建立多少连接。 参数如下所示:

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

如果应用程序的代码按主机名而不是 IP 地址引用应用程序,则 参数应如下所示:

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

最后,如果应用程序托管在 80 以外的端口上,则 参数必须包括 URL 中的非标准端口,如下所示:

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

本文前面讨论的参数设置全部位于进程级别。 但是,参数 maxconnection 设置适用于 AppDomain 级别。 默认情况下,由于此设置适用于 AppDomain 级别,因此最多可以从进程中的每个 AppDomain 创建到特定 IP 地址的两个连接。

ExecutionTimeout

ASP.NET 使用以下配置设置来限制请求执行时间:

<httpRuntime executionTimeout="90"/>

还可以使用 Server.ScriptTimeout 属性设置此限制。

注意

如果增大参数的值 executionTimeout ,可能还需要修改 processModelresponseDeadlockInterval 参数设置。

建议

本部分中建议的设置可能不适用于所有应用程序。 但是,以下附加信息可以帮助你进行适当的调整。

如果要从每个 ASPX 页对单个 IP 地址进行一次 Web 服务调用,Microsoft 建议使用以下配置设置:

  • 将 参数和 maxIoThreads 参数的值maxWorkerThreads设置为 100
  • 将 参数的值 maxconnection 设置为 12*N (其中 N 是) 的 CPU 数。
  • 将 参数的值 minFreeThreads 设置为 88*N ,将 minLocalRequestFreeThreads 参数设置为 76*N
  • 将 的值 minWorkerThreads 设置为 50。 请记住, minWorkerThreads 默认情况下不在配置文件中。 必须添加它。

其中一些建议涉及一个简单的公式,该公式涉及服务器上的 CPU 数。 表示公式中 CPU 数的变量为 N

对于这些设置,如果已启用超线程,则必须使用逻辑 CPU 数而不是物理 CPU 数。 例如,如果启用了超线程的四处理器服务器,则公式中 N 的值将为 8 而不是 4

注意

使用此配置时,每个 CPU 最多可以同时执行 12 个 ASP.NET 请求,因为 100-88=12。 因此,至少有 88*N 个工作线程和 88*N 个完成端口线程可用于其他用途 (例如 Web 服务回调) 。

例如,你有一个启用了四个处理器和超线程的服务器。 根据这些公式,你将对本文中提到的配置设置使用以下值。

<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>

此外,使用此配置时,每个 AppDomain 的每个 CPU 每个 IP 地址提供 12 个连接。 因此,在以下方案中,当请求等待连接并且 ThreadPool 未用尽时,很少发生争用:

  • Web 仅托管一个应用程序 (AppDomain) 。
  • ASPX 页面的每个请求都会发出一个 Web 服务请求。
  • 所有请求都指向同一 IP 地址。

但是,使用此配置时,涉及以下其中一项的方案可能会使用过多连接:

  • 请求发送到多个 IP 地址。
  • 请求 (302 状态代码) 重定向。
  • 请求需要身份验证。
  • 请求来自多个 AppDomain。

在这些方案中,最好对 参数使用较低的值,对maxconnection参数和 minLocalRequestFreeThreads 参数使用较高的值minFreeThreads

更多信息

有关详细信息,请参阅 提高 ASP.NET 性能

如果在 IIS 上遇到性能不佳和争用 ASP.NET,请转到以下 Microsoft 博客: