我正在开发一个基于 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/