【问题标题】:Executing more than 1000 HTTP request tasks fails执行超过 1000 个 HTTP 请求任务失败
【发布时间】:2015-06-17 09:02:22
【问题描述】:

我正在尝试对 API 进行负载测试。我正在同时执行一个任务,每个任务都执行一个 HTTP 请求。我使用Task.WhenAll(mytasks) 等待所有任务完成。请求如下所示:

using (var response = await client.SendAsync(request).ConfigureAwait(false))
{
    using (var jsonResponse = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
    {
        var jsonSerializer = new DataContractJsonSerializer(typeof(Borders));
        var borders = (Borders)jsonSerializer.ReadObject(jsonResponse);
        return borders;
    }
}

这对至少一千个任务都有效。但是,如果我开始执行超过几千个任务,我会遇到HttpRequestExceptions

System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
   --- End of inner exception stack trace ---
   at System.Net.ConnectStream.WriteHeadersCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
   at System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
   at BusinessLogic.<GetBorder>d__6d.MoveNext() in c:\BusinessLogic.cs:line 757

所以我的问题是:为什么会发生这种情况(超过 1000 个任务)?我怎样才能防止这种情况?我显然可以将我的任务块切成

【问题讨论】:

  • 您正在使用 IIS 的 Windows 7 或 8 上进行测试?
  • 异常An existing connection was forcibly closed by the remote host 可能是正在发生的事情的线索。
  • @Max:Windows Server 2008 R2;没有 IIS,我使用的是自托管的 ASP.NET Web Api
  • 没有线索。由于某种原因,服务器可能会阻塞 >1000 个连接。配置?缺乏资源? This 可能会有所帮助。
  • 如果所有这些请求都发往同一台服务器,这可能是服务器限制或服务器限制以防止 DoS 攻击。

标签: c# concurrency task-parallel-library httprequest task


【解决方案1】:

我想把这个留给底层系统...

这不是一个好主意。 .NET Framework 在确定 IO 的最佳并行度方面的能力为零。

并行发出这么多请求通常不是一个好主意,因为在此之前压力很大的资源很可能在此之前就已经用尽了。显然,您的后端服务器无法处理这种程度的并行性。它会根据消息强行将您切断。

仅仅因为我们现在使用 await 提供了简单的异步 IO,并不意味着您可以使用 1000 个并行请求向您的资源发送垃圾邮件。

使用一种常见的解决方案以一定的并行度执行一系列异步操作。我喜欢http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx,但也有基于ActionBlock 的解决方案。

【讨论】:

  • 现在我想想你的解释是有道理的......(不要问我为什么不早点考虑......)。看起来我只是被一个易于使用的 API 困住了。谢谢!
  • .NET 没有一种简单的内置方法来为具有异步和给定 DOP 的每个循环运行并行。这通常是需要的,并且每周会在 Stack Overflow 上引发十几个问题。您并不孤单。
【解决方案2】:

正如 usr 在他的回答中所说,您的服务器正在关闭连接,导致错误消息:

An existing connection was forcibly closed by the remote host.

您仍然不知道您的瓶颈到底是什么。可能是您的服务器无法处理请求,可能是服务器由于某些速率限制功能而阻止了它们,也可能是客户端本身由于无法足够快地处理响应而导致太多连接打开。在优化服务之前,您需要收集更多信息。

我建议不要尝试编写自己的压力测试工具,而是使用已建立的工具,例如 Apache JMeter。您应该能够构建一个类似于您要运行的测试用例的测试计划。

在某些时候,您会达到单台机器无法模拟的用户数。有些服务(如Redline13)可让您从 EC2 实例运行测试,并为您提供分析结果数据的工具。你也可以使用这个JMeter EC2 script 从多台机器上执行你的测试计划。

【讨论】:

    【解决方案3】:

    我前段时间写了这个要点

    //PM> Install-Package Rx-Linq
    readonly List<string> _list = new List<string> { "http://www.google.com", "https://www.gmail.com", "http://www.aripaev.ee" };
    private readonly string format = "[{0}] {1} : {2} [{3}]";
    [Category("WebClient")]
    [TestCase("sync" )]
    public void SynchronousTest(string name)
    {
        DateTime start = DateTime.Now;
        var dict = _list.ToDictionary(o => o, o => new WebClient().DownloadString(new Uri(o)));
        dict.Keys.ToList().ForEach(o => Console.WriteLine(format, DateTime.Now - start, o, dict[o].Length, name));
    }
    [Category("WebClient")]
    [TestCase("async")]
    public void AsynchronousTest(string name)
    {
        DateTime start = DateTime.Now;
        var dict = _list.ToDictionary(o => o, async o => await new WebClient().DownloadStringTaskAsync(new Uri(o)));
        dict.Keys.ToList().ForEach(o => Console.WriteLine(format, DateTime.Now - start, o, dict[o].Result.Length, name));
    }
    [Category("WebClient")]
    [TestCase("lazy")]
    public void LazyTest(string name)
    {
        var start = DateTime.Now;
        var dict = _list.ToDictionary(o => o, o => new Lazy<string>(() => new WebClient().DownloadString(new Uri(o))));
        dict.Keys.ToList().ForEach(o => Console.WriteLine(format, DateTime.Now - start, o, dict[o].Value.Length, name));
    }
    

    结果如下:

    [00:00:00.9520952] http://www.google.com : 51348 [sync]
    [00:00:00.9520952] https://www.gmail.com : 58704 [sync]
    [00:00:00.9520952] http://www.aripaev.ee : 208324 [sync]
    
    [00:00:00.0010001] http://www.google.com : 51412 [lazy]
    [00:00:00.6550655] https://www.gmail.com : 58664 [lazy]
    [00:00:00.7690769] http://www.aripaev.ee : 208324 [lazy]
    
    [00:00:00.1430143] http://www.google.com : 51366 [async]
    [00:00:00.3430343] https://www.gmail.com : 58616 [async]
    [00:00:00.5150515] http://www.aripaev.ee : 208324 [async]
    

    单台机器/服务器可以同时处理 300-500 个请求,但即使这样也是对系统/网络资源的压力测试。

    【讨论】:

      猜你喜欢
      • 2020-03-07
      • 2018-04-18
      • 2013-02-13
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多