2012-03-21 7 views
3

Я разрабатываю фронт-контроллер 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; 
    } 
} 
+1

Каково было ваше решение, в конце концов? Принятый ответ - это ссылка, которая делает что-то совсем другое, чем вы объясняете. –

ответ

0

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

Существует пример делать это на Azure здесь:

https://github.com/richorama/AzureARR

6

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

return proxy.BeginGetResponse(cb, ... 

Приветствия