docs.microsoft.com makes an important observation about HttpClient usage System.Net.Http.HttpClient
HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors. Below is an example using HttpClient correctly.
The language ‘instantiated once and re-used’ invokes in the developer mind, multiple potential solutions. However, the example provided in the documentation may unintentionally mislead developers into making a poor design choice. Take for example the following code sample which describes a design pattern which is in fact, not recommend:
static async Task Main() { // Create a New HttpClient object and dispose it when done, so the app doesn't leak resources using (HttpClient client = new HttpClient()) { //This breaks the guidance given already as it does not create a single instance once and re-use. Underload this may cause resource issues with the application. } }
Our next example resolves one part of our recommendations: creating a single instance but fails to advise us on the practical situation the HttpClient is called regularly from many threads
public class GoodController : ApiController { // OK private static readonly HttpClient HttpClient; static GoodController() { HttpClient = new HttpClient(); } }
This solution may not be the best for applications making multiple calls using the same instance of HttpClient from several threads. For example, one issue that arises using this approach could be that several threads contend for the default headers collection of the same instance of the HttpClient class and create a high CPU condition. Using a single static instance of HttpClient will not fix the situation. The developer requires a different solution. At this point in the discussion the developer may start to read about the HttpClientHandler class.
HttpClientHandler is responsible for managing the underlying connection resources of the HttpClient instances. Again, looking into the documentation examples we do not have any improved details for handling creating a single instance and re-usability with HttpClient:
static async Task Main() { // Create an HttpClientHandler object and set to use default credentials HttpClientHandler handler = new HttpClientHandler(); //once again we create a new instance each time this method is called handler.UseDefaultCredentials = true; // Create an HttpClient object HttpClient client = new HttpClient(handler); //and we continue to avoid following our recommendations }
What then is a good way to both share the HttpClient resources to enable the high scalability and performance while not exhausting system resources but avoiding the problems that come from using the same instance of HttpClient from several threads? One answer is using a shared instance of HttpClientHandler which is passed into an instance of HttpClient and informing the client that it does not own the underlying handler.
public static System.Net.Http.HttpClientHandler GetHttpClientHandler() { if (HttpClientHandler == null) { HttpClientHandler = new System.Net.Http.HttpClientHandler() { MaxConnectionsPerServer = 20 }; } return HttpClientHandler; }
This approach resolves the single instance for the lifetime of the application and still allows for scalability and accessibility from several threads. And it opens the door into finding solutions for other problems with the HttpClient such as, how to properly deal with DNS changes after the client has been instantiated. Without a way of cleaning up a HttpClient or HttpClientHandler after its creation - the moment the client goes bad, if it's not recreated then the application will be stuck until recycled. This is a topic for another chapter but here you can see that using the underlying handler will expose a way to trap for errors and signal to recreate the handler, which will be automatically used on the next HttpClient calls...
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.