3

我正在开发一个基于 Martin Fowler 模式的 HTTP 前端控制器(链接)。在我的情况下,控制器具有以下职责: - 解组封装数据 - 授权请求 - 记录 - 将请求中继/转发到另一台服务器

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

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

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

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

想到的最后一个(未经测试的)解决方案是使用 HttpListener 类。猜测这是更轻量级的解决方案,具有对并发性的细粒度控制。我看过 José F. Romaniello (链接) 使用 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;
    }
}
4

2 回答 2

6

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

return proxy.BeginGetResponse(cb, ...

干杯

于 2012-11-27T21:12:40.957 回答
0

一种可能的解决方案可能是使用 ARR 为您进行转发。

这里有一个在 Azure 上执行此操作的示例:

https://github.com/richorama/AzureARR

于 2012-05-04T10:23:56.237 回答