I’ve been experiencing some port exhaustion recently, and I recently stumbled across this video by Nick Chapsas where he talks about how misusing HttpClient can lead to some of the same issues that I’ve been seeing.
I use the HttpClientFactory when configuring the services like how he recommends, but I’m unsure if sharing this one client among different classes could be coming back to bite me.
I can’t share the exact code, but here’s a recreation of how it’s being used in the application.
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IWebServiceFactory, WebServiceFactory>();
builder.Services.AddHttpClient<IWebServiceFactory, WebServiceFactory>();
var app = builder.Build();
builder.Services.AddControllersWithViews();
... // omitted for berevity
WebServiceFactory.cs
using Test.Services.WebServices;
namespace Test.Services;
public class WebServiceFactory : IWebServiceFactory
{
private readonly HttpClient _httpClient;
public WebServiceFactory(HttpClient httpClient)
{
_httpClient = httpClient;
}
public IWebService GetWebService(WebServiceType webServiceType)
{
return webServiceType switch
{
WebServiceType.WEBSERVICE_TYPE_A => new WebServiceA(_httpClient),
WebServiceType.WEBSERVICE_TYPE_B => new WebServiceB(_httpClient),
WebServiceType.WEBSERVICE_TYPE_C => new WebServiceC(_httpClient),
_ => throw new ArgumentException("Web service not implemented for type")
};
}
}
WebServiceA.cs (WebService B and C are nearly identical)
namespace Test.Services.WebServices;
public class WebServiceA : IWebService
{
private readonly int _timeout = 10_000; // in milliseconds
private readonly HttpClient _httpClient;
public WebServiceA(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string GetResponse(string id)
{
string uri = $"https://fakesiteA.com?id={id}";
HttpRequestMessage request = new HttpRequestMessage();
request.Method = HttpMethod.Get;
request.RequestUri = new Uri(uri);
HttpResponseMessage response = SendRequest(request).Result;
return response.Content.ReadAsStringAsync().Result;
}
private async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage)
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(_timeout));
var response = await _httpClient.SendAsync(requestMessage, cts.Token);
return response;
}
}
WebServiceFactory accepts the HttpClient, and passes it through to WebServiceA, WebServiceB, or WebServiceC depending on the value of WebServiceType.
Was wondering if this implementation had any drawbacks, and could maybe be leading to the port exhaustion issue I’ve been encountering.
Any thoughts?
Edit: should’ve mentioned that the calling code will then call GetResponse(id)
on the webservice after it’s returned. Maybe it was obvious, but figured I should be more explicit.
2
Answers
You could inject
IHttpClientFactory
instead ofHttpClient
which would allow you to create new clients for each service. However, I would suggest you injectIServiceProvider
and use that instead of manually doingnew WebServiceA(...)
.So something like this:
And set up your DI like this:
The benefit of this is each of your services can be pulled from DI so will get their own
HttpClient
and anything else they may need.Side note: Calling
AddHttpClient<T>
overrides a call toAddScoped<T>
and redefines the service as transient.I think your problem is here:
Factories are generally Singletons. You are basically creating a new factory every time a message arrives, which causes the original problem.
Try: