Эффективная переадресация HTTP-запросов с помощью IHttpAsyncHandler
Я разрабатываю контроллер http front, основанный на шаблоне Мартина Фаулера ( link). В моем случае у контролера есть следующие обязанности: - Инкапсулированные данные Unmarshall - Санкционировать запрос - Лесозаготовительный - Ретрансляция / пересылка запроса на другой сервер
На ум пришли следующие возможные решения: - (Синхронно) IHttpHandler, перенаправляющий запрос с помощью класса WebClient или HttpWebRequest - (Асинхронный) IHttpListener (не-IIS решение) - (Асинхронный) IHttpAsyncHandler
Идеальная ситуация заключается в том, что FC может обрабатывать множество параллельных запросов (>10000 TPS) без сжигания процессора.
Для тестирования решений я создал небольшую платформу, которая имеет 3 клиента, выполняющих запросы, передний контроллер, который сидит в середине, и 2 сервера, которые отвечают на запросы, передаваемые FC. Сценарий framework benchmarks 3, Во-первых, он тестирует с быстрыми ответами с небольшими полезными нагрузками, во-вторых: быстрые ответы с большими полезные нагрузки (>100 КБ) и, наконец, его тесты с медленными ответами (>3 секунд) и небольшими полезными нагрузками.
Количество транзакций в секунду (TPS) падает до предельно низкого уровня (
Последнее (непроверенное) решение что пришло на ум, так это использовать класс 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;
}
}
2 ответа:
Одним из возможных решений этого может быть использование ARR для выполнения переадресации для вас.
Вот пример того, как это делается в Azure:
В
return proxy.BeginGetResponse(new AsyncCallback(EndProcessRequest), ...
вы создаете новый обратный вызов, и ASP.net не получает информации об окончании запроса, поэтому он не может остановить обработку запроса (это по крайней мере то, что я наблюдал по многим параллельным запросам). Поэтому используйте обратный вызов, переданный в качестве аргумента.return proxy.BeginGetResponse(cb, ...
Ура