IHttpHandler与HttpTaskAsyncHandler的性能

Twi*_*ron 7 .net c# benchmarking performance-testing apachebench

我们有一个webapp,它通过.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; } }
}
Run Code Online (Sandbox Code Playgroud)

异步处理程序

<%@ 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; } }
}
Run Code Online (Sandbox Code Playgroud)

标杆

我使用apache基准实用程序运行了一些基准测试.这是我正在使用的命令(显然,改变下面结果的数字).

ab -n 1000 -c 10 http://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请求/秒

正如您所看到的,同步与异步似乎几乎没有任何影响(至少不足以使其值得切换).

我的问题是:在我的测试中是否弄乱了一些不能准确建模这个概念的东西?

Twi*_*ron 7

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个并发请求.对于异步,它是301,这意味着async/await在处理非阻塞调用时肯定会产生更高的吞吐量.