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 ожидается

Другие вопросы по тегам