5

I'm having an issue with HttpClient and async requests. Basically i'm having an async method that is creating async requests with a shared HttpClient that is initialized in the ctor.

My problem is that it seems that the HttpClient blocks when calling my method in an async manner.

Here is my calling code:

var tasks = trips.Select(u => api.Animals.GetAsync(u * 100, 100).ContinueWith(t =>
        {
            lock (animals)
            {
                if (t.Result != null)
                {
                    foreach (var a in t.Result)
                    {
                        animals.Add(a);
                    }
                }
            }
        }));
        await Task.WhenAll(tasks);

Here is the method that blocks with a shared HttpClient:

 //HttpClient blocks on each request
                var uri = String.Format("animals?take={0}&from={1}", take, from);
                var resourceSegmentUri = new Uri(uri, UriKind.Relative);

                var response = await _client.GetAsync(resourceSegmentUri);

                if (response.IsSuccessStatusCode)
                {
                    var content = await response.Content.ReadAsStringAsync();

                    var animals = JsonConvert.DeserializeObject<T>(content);

                    return animals;
                }

This snippet does not block, when using a client for each request:

using (var client = new HttpClient(){BaseAddress = new Uri(_config.BaseUrl)})
{
    var uri = String.Format("animals?take={0}&from={1}", take, from);
    var resourceSegmentUri = new Uri(uri, UriKind.Relative);

    var response = await client.GetAsync(resourceSegmentUri);

    if (response.IsSuccessStatusCode)
    {
        var content = await response.Content.ReadAsStringAsync();

        var animals = JsonConvert.DeserializeObject<T>(content);

        return animals;
    }
}

Is a shared HttpClient a no go? Or can I utilize it in some other way?

Bj Blazkowicz
  • 1,157
  • 9
  • 20
  • 1
    What is the advantage you gain for sharing the HttpClient? Just to save from reconstructing the object? – Mitchell Lee Jul 01 '13 at 07:51
  • 1
    Yes, pretty much... Now when you say it, maybe I should just not bother? On some clients I would like to add some custom headers in the ctor. Then it seems bad to do it again in the methods. – Bj Blazkowicz Jul 01 '13 at 07:53
  • 1
    OK, how about creating and keeping a pool of them in some container, a queue,say, and handing them out to tasks as required. You can push them back onto the queue as the tasks finish. – Martin James Jul 01 '13 at 08:02
  • 1
    Or you could create an HttpClient factory to handle their individual lifecycles, and centralize initializing headers and the like, to avoid duplication. – Mitchell Lee Jul 01 '13 at 08:09
  • Sure, but it does not really match my design pattern. And it seems like an approach that is too complex for the overall design of my program. Basically im having X number of classes that match a REST-resource, each with a custom made HttpClient that is initilized in the ctor. The HttpClients in the classes could just be cloned or something in the scope of each class because they are equally comparable. – Bj Blazkowicz Jul 01 '13 at 08:17
  • But maybe the HttpClientFactory would be something. Hmm – Bj Blazkowicz Jul 01 '13 at 08:25
  • 1
    Are you absolutely sure `HttpClient` is blocking and non-blocking as you describe? Is it possible you're actually seeing throttling (`ServicePointManager.DefaultConnectionLimit`) in your "blocking" test, and cache results in your "non-blocking" test? – Stephen Cleary Jul 01 '13 at 10:40
  • Yes I think I am pretty sure. Maybe it has something to do with max allowed connections per domain? Is there some setting like that? – Bj Blazkowicz Jul 01 '13 at 13:20
  • As the HttpClient class internally uses pooled connections you might want to remove the shared instance. Creating a new object isn't that much overhead and the connections are pooled between multiple instances automatically. See the ServicePoint class for more information. – Dominik Weber Jul 01 '13 at 16:16
  • If you're using a shared HTTPClient is it possible that HTTPClient has to wait for the first request to complete before moving on to the next? – James Jeffery Aug 21 '13 at 20:04
  • Using a shared HttpClient is actually recommended. See my answer why here - https://stackoverflow.com/questions/22560971/what-is-the-overhead-of-creating-a-new-httpclient-per-call-in-a-webapi-client/35045301#35045301 – Dave Black Nov 09 '17 at 20:12

1 Answers1

2

Using a shared HttpClient is actually recommended.

See my answer why - What is the overhead of creating a new HttpClient per call in a WebAPI client?

Dave Black
  • 7,305
  • 2
  • 52
  • 41
  • Yes, since I posted this I have started using a shared HttpClient. In order to make it do more concurrent requests you can utilize the DefaultConnectionLimit property in the ServicePointManager. Which is defaulted to 2. I usually set it to a larger value, like 50 or so. – Bj Blazkowicz Nov 14 '17 at 15:08