【问题标题】:Why is there a threshold of concurrent HTTP requests I can make using `HttpClient`?为什么我可以使用 `HttpClient` 发出并发 HTTP 请求的阈值?
【发布时间】:2019-09-05 22:26:11
【问题描述】:

我注意到我可以使用 .NET 核心的HttpClient 发出的并发 HTTP 请求存在某种阈值,即当我有

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);
            }
        }
    }
}

,例外情况:

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

还有一些关于我的机器的信息:

$ 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

【问题讨论】:

标签: c# .net .net-core dotnet-httpclient


【解决方案1】:

“对等方重置连接”指向另一端,而不是您的代码,正在断开连接。

【讨论】:

  • 而且代码相当于拒绝服务攻击。
  • @DavidBrowne-Microsoft 我明白了。这只是一个从同一个example.com 域请求标头信息的玩具示例。我最初的用例是请求给定域上各种资源的标头信息,以查看该资源是否存在。为了避免 DoS 攻击服务器,我该怎么办?也许错开我同时提出的请求数量?有什么好的方法呢?
  • @tymtam 给定一个 URL 列表(必须在同一个域上提供),确定是否存在任何 URL。我通过向每个 URL 发送 HTTP HEAD 请求并查看是否收到 200 响应来执行此操作。
  • 连接需要资源,例如ephemeral ports。这些数量有限。它们通常有一个超时时间(例如捕获任何延迟的数据包),这意味着它们在使用后最多保留 120 秒。这些限制不仅适用于您的客户端,也不仅适用于服务器,还适用于介于两者之间的任何代理或网络转换节点。我建议您通过建立一个连接池并以有限的速率通过它运行您的事务,从而将自己一次限制为 1,000 个请求。
  • @JohnWu 感谢您的回答。您能指出我可以阅读有关连接池的资源吗?任何与 .NET 相关的特定实现也会很有用。再次感谢。
【解决方案2】:

此代码打开了太多与目标 Web 服务器的并发连接,可能会触发反拒绝服务保护。有一种简单的方法可以将并发请求限制到 .NET Framework 中的任何目标 ServicePoint,并且存在默认限制。

在 .NET Core 中不使用 ServicePoint。你使用HttpClientHandler设置限制:

        var url = "http://example.com";
        HttpClientHandler handler = new HttpClientHandler();
        handler.MaxConnectionsPerServer = 10;
        Client = new HttpClient(handler);

【讨论】:

  • 您能否支持您的说法,即在 .NET Core 上允许的并发请求数是无限的?我正在查看ServicePointManager.DefaultConnectionLimit 的文档,看起来它设置为2docs.microsoft.com/en-us/dotnet/api/…
  • 我记错了。 .NET Core 会简单地忽略 ServicePoint,并限制与 HttpClientHandler 的连接。见编辑。
  • 太棒了!感谢您的帮助:)
猜你喜欢
  • 2020-04-18
  • 2017-10-05
  • 1970-01-01
  • 2020-09-13
  • 2021-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-13
相关资源
最近更新 更多