从 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 队列的任何工作项都将被阻止,直到线程变为可用为止。

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

解决方法

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

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

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

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

注意

将每个 CPU ASP.NET 请求数限制为 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

默认情况下, minWorkerThreads 该参数不存在于 Web.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 地址,Microsoft建议使用以下配置设置:

  • 将参数的值maxWorkerThreads和参数设置为 100maxIoThreads
  • 将参数的值 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 的每个 IP 地址每个 CPU 提供 12 个连接。 因此,在以下方案中,当请求等待连接 ThreadPool 且未用尽时,很少发生争用:

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

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

  • 请求针对多个 IP 地址。
  • 请求被重定向(302 状态代码)。
  • 请求需要身份验证。
  • 请求来自多个 AppDomains。

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

详细信息

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

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