IHttpHandler 与 HttpTaskAsyncHandler 性能对比

IHttpHandler versus HttpTaskAsyncHandler performance

我们有一个网络应用程序,出于 CORS 和安全目的,通过 .NET IHttpHandler(称为 proxy.ashx)路由许多请求。一些资源加载速度快,而另一些资源加载速度慢,这取决于这些资源所需的大量计算。这是预期的。

在重负载期间,proxy.ashx 速度变慢,所有资源都需要很长时间才能加载。在这些高峰加载时间,如果您绕过代理并直接加载资源,它会立即加载,这意味着代理是瓶颈。
(即 http://server/proxy.ashx?url=http://some_resource loads slow, but http://some_resource 自行加载速度很快)。

我假设响应速度降低是因为 IHttpHandler 是同步编码的,当有太多 long-运行 请求处于活动状态时,IIS 请求线程都忙。我创建了一个快速 A/B 测试应用程序来验证我的假设,我的测试结果表明情况并非如此。

This article 是我理解请求线程池的基础。

On the Web server, the .NET Framework maintains a pool of threads that are used to service ASP.NET requests. When a request arrives, a thread from the pool is dispatched to process that request. If the request is processed synchronously, the thread that processes the request is blocked while the request is being processed, and that thread cannot service another request. ...
However, during an asynchronous call, the server is not blocked from responding to other requests while it waits for the first request to complete. Therefore, asynchronous requests prevent request queuing when there are many requests that invoke long-running operations.

在我下面的示例中,理论上,同步处理程序应该在某个阈值后占用请求线程,以防止启动更多新请求。异步处理程序应该允许更多的请求排队,因为每个请求在等待 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 10 http://localhost/AsyncProxyTest/Sync.ashx
ab -n 1000 -c 10 http://localhost/AsyncProxyTest/Async.ashx

结果

1,000 个请求,一次 10 个

10,000 个请求,一次 100 个

10,000 个请求,一次 1,000 个

如您所见,同步与异步似乎几乎没有任何影响(至少不足以值得切换)。

我的问题是:我是不是在测试中搞砸了一些没有准确建模这个概念的东西?

桌面版 IIS 存在一个限制,即一次将并发请求限制为 10 个(请参阅 )。此限制不存在于 IIS Express 中,也不存在于 Windows 服务器上的 IIS 中。

测试没有问题,它们只需要 运行 在不受限制的网络服务器上。我在 Windows 服务器上使用 IIS 重新 运行 这些测试,我的发现完全符合我最初假设的预期。

再次显示结果。 (注意:这些结果来自非常活跃的开发服务器,因此可能会有一些波动

1,000 个请求,一次 10 个
(这些结果是相同的,因为我们没有超过请求限制)

  • 同步:31.88 requests/second
  • 异步:29.13 request/second

10,000 个请求,一次 100 个

  • 同步:68.53 requests/second
  • 异步:310.66 requests/second

10,000 个请求,一次 1,000 个

  • 同步:55.09 requests/second
  • 异步:669.41 requests/second

我捕获的另一个指标是最大并发请求数 运行ning。这就是我发现本地机器 10 的限制的方式。在 运行 在 Windows 服务器上再次测试后,同步的最大值为 ~48 个并发请求。对于异步,它是 301,这意味着 async/await 在处理非阻塞调用时肯定会产生更高的吞吐量。