Я разрабатываю фронт-контроллер HTTP на основе шаблона Martin Fowler (link). В моем случае контроллер имеет следующие обязанности: - маршализация инкапсулированную данные - Разрешает запрос - Logging - Реле/переслать запрос на другой серверЭффективная пересылка HTTP-запросов с помощью IHttpAsyncHandler
Ниже возможные решения пришли на ум: - (синхронные) IHttpHandler, направляет запрос с WebClient или класса HttpWebRequest - (Asynchronous) IHttpListener (не IIS раствор) - (Asynchronous) IHttpAsyncHandler
Идеальная ситуация в том, что ФК может обрабатывать большое количество одновременных запросов (> 10000 TPS) без сжигания процессора.
Для проверки решений я создал небольшую инфраструктуру, в которой есть 3 клиента, выполняющие запросы, фронт-контроллер, который находится на среднем и 2 серверах, которые отвечают запросам, переданным FC. Сценарий рамочных тестов 3, во-первых, он проверяет быстрые ответы с небольшими полезными нагрузками, во-вторых: быстрые ответы с большими полезными нагрузками (> 100 КБ) и, наконец, его тесты с медленными ответами (> 3 секунды) и небольшими полезными нагрузками.
Операции в секундах (TPS) снижаются до конечного минимума (< 25 TPS) с последним тестом с синхронными обработчиками HTTP. Я предполагаю, что это связано с тем, что обработчик блокирует поток, когда ожидает ответа. Чтобы преодолеть эту проблему, я начал реализовывать асинхронный обработчик (см. Код ниже). Проблема в том, что она просто не работает.
Последнее (непроверенное) решение, которое пришло в голову, - использовать класс HttpListener. Угадав, что это более легкое решение с тонким контролем зерна для параллелизма. Я видел пример реализации с использованием RX-рамки Хосе Ф. Романелло (link).
Мой вопрос будет таким, почему не работает код обработчика ?, это самый эффективный способ сделать это? или я должен быть в пользу решения 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;
}
}
Каково было ваше решение, в конце концов? Принятый ответ - это ссылка, которая делает что-то совсем другое, чем вы объясняете. –