HttpClient pipelining HTTP GET requests to ServiceStack API

1.9k views Asked by At

First, I have ServiceStack as my server which provides RESTful HTTP API.

Here is an example.

public void GET(XXXXXRequest request)
{
    System.Threading.Thread.Sleep(2000);
}

Then I use System.Net.Http.HttpClient to access it. As it is said here, HttpClient is thread-safe for most of its methods to sending HTTP GET requests over the same TCP connection.

So I have a singleton instance of HttpClient as below

HttpClient _httpClient = new HttpClient(new WebRequestHandler()
{
    AllowPipelining = true
});

Then I use the following test code to send the request one after previous's response

await _httpClient.SendAsync(request1, HttpCompletionOption.ResponseContentRead);
await _httpClient.SendAsync(request2, HttpCompletionOption.ResponseContentRead);
await _httpClient.SendAsync(request3, HttpCompletionOption.ResponseContentRead);

In smart sniffer, I do see the requests are sent in one connection and it goes like:

Client -> Request1
Client <- Response1
Client -> Request2
Client <- Response2
Client -> Request3
Client <- Response3

Now I change the code to fire-and-forget mode as below.

_httpClient.SendAsync(request1, HttpCompletionOption.ResponseContentRead);
_httpClient.SendAsync(request2, HttpCompletionOption.ResponseContentRead);
_httpClient.SendAsync(request3, HttpCompletionOption.ResponseContentRead);

So that the requests are sent without waiting for previous reponse and I expect the requests & response go like below

Client -> Request1
Client -> Request2
Client -> Request3
Client <- Response1
Client <- Response2
Client <- Response3

This is HTTP pipeline and quite good for performance.

enter image description here

But from my test, I see 3 connections are established for each of the HTTP GET request and it does not work as I expected.

Regarding the AllowPipelining proeprty, MSDN says

An application uses the AllowPipelining property to indicate a preference for pipelined connections. When AllowPipelining is true, an application makes pipelined connections to the servers that support them.

So, I suppose HttpClient does support pipelining, and the problem is located in ServiceStack? Is there some options in ServiceStack to enable HTTP pipelining?

2

There are 2 answers

2
Todd Menier On

Pipelining is done at a very low level, below IIS even (in the http.sys kernel mode driver). While I'm afraid I can't explain the behavior you're seeing, I can confidently say that ServiceStack is not on the hook for supporting it. It's an HttpHandler, whose only concern is how to process a request and return a response.

0
berkeleybross On

When you use multiple await operators, they are run one after another, so HttpClient is only given one request at a time, and therefore can't use pipelining.

You can use Task.WhenAll() to run multiple tasks in parallel:

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];

Notice that only the Task.WhenAll is awaited