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

2 ответа:

Одним из возможных решений этого может быть использование ARR для выполнения переадресации для вас.

Вот пример того, как это делается в Azure:

Https://github.com/richorama/AzureARR

В return proxy.BeginGetResponse(new AsyncCallback(EndProcessRequest), ... вы создаете новый обратный вызов, и ASP.net не получает информации об окончании запроса, поэтому он не может остановить обработку запроса (это по крайней мере то, что я наблюдал по многим параллельным запросам). Поэтому используйте обратный вызов, переданный в качестве аргумента.

return proxy.BeginGetResponse(cb, ...

Ура