Reputation: 500
I noticed that there exists some kind of threshold for concurrent HTTP requests I can making using .NET core's HttpClient
i.e. it seems to work fine when I have <= 1,000 requests, but nearing 10,000 is problematic. Here is the relevant failing code:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace RequestsGalore
{
class Program
{
static HttpClient Client { get; set; } = new HttpClient();
static void Main(string[] args)
{
var url = "http://example.com";
int requests = 10_000;
var tasks = new Task<HttpResponseMessage>[requests];
for (int i = 0; i < requests; i++)
{
tasks[i] = Client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
}
Task.WaitAll(tasks);
for (int i = 0; i < requests; i++)
{
Console.WriteLine(tasks[i].Result.StatusCode);
}
}
}
}
, and the exception:
Unhandled Exception: System.AggregateException: One or more errors occurred.
(An error occurred while sending the request.)
(A task was canceled.)
.
. [MANY OF THE ABOVE TWO MESSAGES]
.
---> System.Net.Http.HttpRequestException: An error occurred while sending the request.
---> System.IO.IOException: Unable to read data from the transport connection: Connection reset by peer.
---> System.Net.Sockets.SocketException: Connection reset by peer
--- End of inner exception stack trace ---
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error)
at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token)
at System.Net.Http.HttpConnection.FillAsync()
at System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(Boolean foldedHeadersAllowed)
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task.WaitAllCore(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
at RequestsGalore.Program.Main(String[] args) in /home/[REDACTED]/Downloads/RequestsGalore/Program.cs:line 27
And some information about my machine:
$ dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 2.2.401
Commit: 729b316c13
Runtime Environment:
OS Name: ubuntu
OS Version: 19.04
OS Platform: Linux
RID: ubuntu.19.04-x64
Base Path: /usr/share/dotnet/sdk/2.2.401/
Host (useful for support):
Version: 2.2.6
Commit: 7dac9b1b51
.NET Core SDKs installed:
2.2.401 [/usr/share/dotnet/sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.2.6 [/usr/share/dotnet/shared/Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.2.6 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.2.6 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
Upvotes: 0
Views: 3318
Reputation: 89141
This code opens too many concurrent connections to the target web server, probably triggering anti-denial-of-service protections. There's a simple way to limit the concurrent requests to any target in .NET Framework the ServicePoint, and there are default limits in place.
In .NET Core ServicePoint is not used. And you set the limit using the HttpClientHandler:
var url = "http://example.com";
HttpClientHandler handler = new HttpClientHandler();
handler.MaxConnectionsPerServer = 10;
Client = new HttpClient(handler);
Upvotes: 3
Reputation: 35037
"Connection reset by peer" points to the other end, not your code, dropping the connection.
Upvotes: 4