6

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?

Community
  • 1
  • 1
Mr.Wang from Next Door
  • 13,670
  • 12
  • 64
  • 97

2 Answers2

2

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.

Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • I am using self-host service `AppSelfHostBase` instead of IIS, I assume it is done via `http.sys` and `HttpListener` internally? – Mr.Wang from Next Door Aug 14 '15 at 02:44
  • Yes. ServiceStack's [listener](https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/Host/HttpListener/HttpListenerBase.cs) uses System.Net.HttpListener internally. – Todd Menier Aug 14 '15 at 12:58
0

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

berkeleybross
  • 1,314
  • 2
  • 13
  • 27