c# - 使用 IHttpAsyncHandler 高效转发 HTTP 请求

标签 c# .net ihttphandler front-controller

我正在开发一个基于 Martin Fowler (link) 模式的 HTTP 前端 Controller 。就我而言, Controller 具有以下职责: - 解码封装数据 - 授权请求 - 记录 - 将请求中继/转发到另一台服务器

想到以下可能的解决方案: - (同步)IHttpHandler,用WebClient或HttpWebRequest类转发请求 - (异步)IHttpListener(非 IIS 解决方案) - (异步)IHttpAsyncHandler

理想情况是 FC 可以处理大量并发请求 (>10000 TPS) 而不会消耗 CPU。

为了测试解决方案,我创建了一个小型框架,其中有 3 个发出请求的客户端、一个位于中间的前端 Controller 和 2 个响应 FC 传递的请求的服务器。该框架对 3 种场景进行了基准测试,首先是使用小负载进行快速响应测试,其次是使用大负载(> 100KB)进行快速响应测试,最后是使用慢速响应(> 3 秒)和小负载进行测试。

在使用同步 HTTP 处理程序进行的最后一次测试中,每秒事务处理量 (TPS) 降至最低值 (<25 TPS)。我的猜测是这是由于处理程序在等待响应时阻塞了线程。为了克服这个问题,我开始实现一个异步处理程序(参见下面的代码)。问题是,它简单行不通。

想到的最后一个(未经测试的)解决方案是使用 HttpListener 类。猜测这是一个更轻量级的解决方案,具有对并发的细粒度控制。我看过 José F. Romaniello ( link ) 使用 RX 框架的示例实现。

我的问题是,为什么处理程序代码不起作用?这是最有效的方法吗?或者我应该赞成 HttpListener 解决方案。

异步 ​​HTTP 处理程序代码:

public class ForwardRequestHandler : IHttpAsyncHandler
{
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
        var uri = GetForwardUriFor(context.Request.Url.PathAndQuery);

        var proxy = HttpWebRequest.Create(uri) as HttpWebRequest;
        return proxy.BeginGetResponse(new AsyncCallback(EndProcessRequest), new ForwardedRequestContext(context, proxy));
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        var proxy = result.AsyncState as ForwardedRequestContext;
        proxy.TransferResponse(result);
    }

    public bool IsReusable
    {
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        throw new NotSupportedException();
    }

    private Uri GetForwardUriFor(string path)
    {
        var loadbalancer = new RoundRobinLoadBalancer();
        var endpoint = loadbalancer.GetRandomEndPoint();

        return new Uri(
            string.Format("http://{0}{1}", endpoint, path)
        );
    }
}

public class ForwardedRequestContext
{
    private readonly HttpContext context;
    private readonly HttpWebRequest forwarder;

    public ForwardedRequestContext(HttpContext context, HttpWebRequest forwarder)
    {
        this.context = context;
        this.forwarder = forwarder;
    }

    public void TransferResponse(IAsyncResult ar)
    {
        var response = GetResponse();

        var result = forwarder.EndGetResponse(ar);
        response.StatusCode = 200;
        response.ContentType = result.ContentType;
        response.AddHeader("Content-Length", result.ContentLength.ToString());

        result.GetResponseStream().CopyTo(response.OutputStream);
        response.Flush();

        result.Close();
    }

    private HttpResponse GetResponse()
    {
        return context.Response;
    }
}

最佳答案

return proxy.BeginGetResponse(new AsyncCallback(EndProcessRequest), ... 中,您创建了一个新的回调,并且 ASP.net 没有收到有关请求结束的通知,因此它可以'不要停止处理 thre 请求(这至少是我从许多并发请求中观察到的)。因此请改用作为参数传入的回调。

return proxy.BeginGetResponse(cb, ...

干杯

关于c# - 使用 IHttpAsyncHandler 高效转发 HTTP 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9800347/

相关文章:

c# - 如何设置 IHttpAsyncHandler?

c# - 执行 IHttpHandler 到 MemoryStream

asp.net - 使用 HTTP 处理程序 ASP.Net 调整图像大小,一些图像未显示

javascript - 如何在 C# google 组织结构图中仅显示 1 项?

c# - 在 C# 中测试对象是否为泛型类型

c# - 为什么.NET Framework 指定标准委托(delegate)(Action)?

c# - 验证对象在 Rhino Mocks 中调用自身

c# - 提交点击并重定向时直接下载 pdf

c# - WPF ComboBox IsSynchronised 默认值

c# - 如何在 RavenDB 中获取嵌入式文档属性的聚合数据