Эффективная переадресация 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, ...Ура