We are working on a fun project with a friend and we have to execute hundreds of HTTP requests, all using different proxies. Imagine that it is something like the following:
for (int i = 0; i < 20; i++)
{
HttpClientHandler handler = new HttpClientHandler { Proxy = new WebProxy(randomProxy, true) };
using (var client = new HttpClient(handler))
{
using (var request = new HttpRequestMessage(HttpMethod.Get, "http://x.com"))
{
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
}
using (var request2 = new HttpRequestMessage(HttpMethod.Get, "http://x.com/news"))
{
var response = await client.SendAsync(request2);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
}
}
}
}
By the way, we are using .NET Core (Console Application for now). I know there are many threads about socket exhaustion and handling DNS recycling, but this particular one is different, because of the multiple proxy usage.
If we use a singleton instance of HttpClient, just like everyone suggests:
- We can't set more than one proxy, because it is being set during HttpClient's instantiation and cannot be changed afterwards.
- It doesn't respect DNS changes. Re-using an instance of HttpClient means that it holds on to the socket until it is closed so if you have a DNS record update occurring on the server the client will never know until that socket is closed. One workaround is to set the
keep-alive
header tofalse
, so the socket will be closed after each request. It leads to a sub-optimal performance. The second way is by usingServicePoint
:
ServicePointManager.FindServicePoint("http://x.com")
.ConnectionLeaseTimeout = Convert.ToInt32(TimeSpan.FromSeconds(15).TotalMilliseconds);
ServicePointManager.DnsRefreshTimeout = Convert.ToInt32(TimeSpan.FromSeconds(5).TotalMilliseconds);
On the other hand, disposing HttpClient (just like in my example above), in other words multiple instances of HttpClient, is leading to multiple sockets in TIME_WAIT
state. TIME_WAIT indicates that local endpoint (this side) has closed the connection.
I'm aware of SocketsHttpHandler
and IHttpClientFactory
, but they can't solve the different proxies.
var socketsHandler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10),
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(5),
MaxConnectionsPerServer = 10
};
// Cannot set a different proxy for each request
var client = new HttpClient(socketsHandler);
What is the most sensible decision that can be made?
ServicePointManager
doesn't affectHttpClient
in .NET Core because it intended for use withHttpWebRequest
which is not used byHttpClint
in .NET Core. And yes,HttpClient
instance per proxy looks like reasonable solution.IHttpClientFactory
will fix socket and dns problem at the same time. – Cocknetstat -ano | findstr ip
– KareeHttpResponseMessage
isIDisposable
. Applyusing
statement for it. It will affect sockets utilization behavior. – Cockusing HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
(C# 8.0 syntax) – CockHttpCompletionOption.ResponseHeadersRead
. – Karee