我们有一个Web应用程序,它通过.NET IHttpHandler(称为proxy.ashx)路由许多请求,用于CORS和安全目的。某些资源加载速度快,而其他资源加载速度较慢,具体取决于这些资源所需的大量计算。这是意料之中的。
在负载繁重期间,proxy.ashx 会减慢到爬行,并且所有资源都需要永远加载。在这些高峰加载时间,如果绕过代理并直接加载资源,它会立即加载,这意味着代理是瓶颈。
(即 http://server/proxy.ashx?url=http://some_resource 加载速度慢,但 http://some_resource 本身加载速度很快)。
我有一个假设,响应能力降低是因为 IHttpHandler 是同步编码的,当太多长时间运行的请求处于活动状态时,IIS 请求线程都处于繁忙状态。我创建了一个快速的A/B测试应用程序来验证我的假设,我的测试结果显示情况并非如此。
本文是我了解请求线程池的基础。
在 Web 服务器上,.NET Framework 维护一个线程池,该线程池 用于为 ASP.NET 请求提供服务。当请求到达时,线程 从池中调度以处理该请求。如果请求是 同步处理,处理请求的线程是 在处理请求时被阻止,并且该线程无法 服务另一个请求。 ...
但是,在异步调用期间,不会阻止服务器 在等待第一个请求时响应其他请求 完成。因此,异步请求会阻止请求排队 当有许多请求调用长时间运行的操作时。
在下面的示例中,理论上,同步处理程序应该在某个阈值之后占用请求线程,从而防止启动更多新请求。异步处理程序应该允许更多的请求排队,因为每个请求在等待Task.Delay
时几乎立即将其请求线程返回线程池,允许该请求线程在上一个请求仍在等待时处理新请求。
同步 HttpHandler
<%@ WebHandler Language="C#" Class="SyncHandler" %>
using System.Web;
using System.Threading;
public class SyncHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
//BLOCKING artifical pause to simulate network activity
Thread.Sleep(300);
var Response = context.Response;
Response.Write("sync response");
}
public bool IsReusable { get { return true; } }
}
异步处理程序
<%@ WebHandler Language="C#" Class="AsyncHandler" %>
using System.Web;
using System.Threading.Tasks;
public class AsyncHandler : HttpTaskAsyncHandler
{
public override async Task ProcessRequestAsync(HttpContext context)
{
//NON-BLOCKING artificial pause to simulate network activity
await Task.Delay(300);
var Response = context.Response;
Response.Write("async response");
}
public override bool IsReusable { get { return true; } }
}
标杆
我使用 apache 基准测试实用程序运行了一些基准测试。这是我正在使用的命令(显然更改了以下结果的数字)。
ab -n 1000 -c 10http://localhost/AsyncProxyTest/Sync.ashx
ab -n 1000 -c 10 http://localhost/AsyncProxyTest/Async.ashx
结果
1,000 个请求,一次 10
个- 同步:30.10 个请求/秒
- 异步:32.05 个请求/秒
10,000 个请求,一次 100
个- 同步:33.02 个请求/秒
- 异步:32.05 个请求/秒
10,000 个请求,一次 1,000
个- 同步:32.55 个请求/秒
- 异步:32.05 个请求/秒
如您所见,同步与异步似乎几乎没有影响(至少不足以使其值得切换)。
我的问题是:我在测试中是否搞砸了一些没有准确建模这个概念的东西?
IIS 的桌面版本存在一个限制,一次将并发请求限制为 10 个(请参阅这篇文章)。此限制在 IIS Express 中不存在,在 Windows 服务器上的 IIS 中也不存在。
测试没有错,它们只需要在不受限制的 Web 服务器上运行即可。我在Windows Server上使用IIS重新运行了这些测试,我的发现与我最初的假设完全一样。
这里再次是结果。(注意:这些结果来自非常活跃的开发服务器,因此可能会有一些波动)
1,000 个请求,一次
10 个(这些结果是相同的,因为我们没有超过请求限制)
- 同步:31.88 个请求/秒
- 异步:29.13 个请求/秒
10,000 个请求,一次 100
个- 同步:68.53 个请求/秒
- 异步:310.66 个请求/秒
10,000 个请求,一次 1,000
个- 同步:55.09 个请求/秒
- 异步:669.41 个请求/秒
我捕获的另一个指标是运行的最大并发请求数。这就是我发现本地计算机限制 10 的方式。在 Windows 服务器上再次运行测试后,最大同步请求数为 ~48 个并发请求。对于 async,它是 301,这意味着 async/await 在处理非阻塞调用时肯定会产生更高的吞吐量。