HttpClient конвейеризация HTTP GET-запросов к ServiceStack API
Во-первых, в качестве сервера используется ServiceStack, который предоставляет RESTful HTTP API.
Вот пример.
public void GET(XXXXXRequest request)
{
System.Threading.Thread.Sleep(2000);
}
Тогда я использую System.Net.Http.HttpClient
чтобы получить к нему доступ. Как сказано здесь, HttpClient
для большинства своих методов поточно-ориентирован на отправку HTTP-запросов GET по тому же TCP-соединению.
Итак, у меня есть экземпляр HttpClient, как показано ниже
HttpClient _httpClient = new HttpClient(new WebRequestHandler()
{
AllowPipelining = true
});
Затем я использую следующий тестовый код, чтобы отправить запрос один после предыдущего ответа
await _httpClient.SendAsync(request1, HttpCompletionOption.ResponseContentRead);
await _httpClient.SendAsync(request2, HttpCompletionOption.ResponseContentRead);
await _httpClient.SendAsync(request3, HttpCompletionOption.ResponseContentRead);
В Smart Sniffer я вижу запросы отправляются в одном соединении, и это выглядит так:
Client -> Request1
Client <- Response1
Client -> Request2
Client <- Response2
Client -> Request3
Client <- Response3
Теперь я изменяю код на режим "забей и забудь", как показано ниже.
_httpClient.SendAsync(request1, HttpCompletionOption.ResponseContentRead);
_httpClient.SendAsync(request2, HttpCompletionOption.ResponseContentRead);
_httpClient.SendAsync(request3, HttpCompletionOption.ResponseContentRead);
Так что запросы отправляются без ожидания предыдущего ответа, и я ожидаю, что запросы и ответы идут, как показано ниже
Client -> Request1
Client -> Request2
Client -> Request3
Client <- Response1
Client <- Response2
Client <- Response3
Это HTTP-конвейер и достаточно хорош для производительности.
Но из моего теста я вижу 3 соединения для каждого HTTP-запроса GET, и он не работает так, как я ожидал.
Учитывая AllowPipelining
proseprty, MSDN говорит
Приложение использует свойство AllowPipelining, чтобы указать предпочтение для конвейерных соединений. Когда AllowPipelining имеет значение true, приложение создает конвейерные соединения с серверами, которые их поддерживают.
Итак, я полагаю HttpClient
поддерживает конвейерную работу, и проблема находится в ServiceStack? Есть ли какие-либо опции в ServiceStack для включения конвейерной передачи HTTP?
2 ответа
Конвейерная обработка выполняется на очень низком уровне, даже ниже IIS (в http.sys
драйвер режима ядра). Хотя я боюсь, что не могу объяснить поведение, которое вы видите, я могу с уверенностью сказать, что ServiceStack не на крючке для его поддержки. Это HttpHandler, единственная задача которого - как обработать запрос и вернуть ответ.
Когда вы используете несколько await
операторы, они запускаются один за другим, поэтому HttpClient получает только один запрос за раз и поэтому не может использовать конвейерную обработку.
Вы можете использовать Task.WhenAll() для параллельного запуска нескольких задач:
var responses = await Task.WhenAll(
_httpClient.SendAsync(request1, HttpCompletionOption.ResponseContentRead),
_httpClient.SendAsync(request2, HttpCompletionOption.ResponseContentRead),
_httpClient.SendAsync(request3, HttpCompletionOption.ResponseContentRead));
var response1 = responses[0];
var response2 = responses[1];
var response3 = responses[2];
Обратите внимание, что только Task.WhenAll
ожидается