执行超过1000个HTTP请求任务失败

时间:2015-06-17 09:02:22

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

我正在尝试加载测试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个任务)?我怎么能阻止这个?我显然可以将我的任务块切成块<1000;但我想把它留给底层系统......

3 个答案:

答案 0 :(得分:3)

  

我想把它留给底层系统......

那不是个好主意。 .NET Framework在确定IO的最佳并行度方面没有任何能力。

并行发出许多请求通常不是一个好主意,因为此处强调的资源可能在此之前被最大化。显然,你的后端服务器不能处理这种程度的并行性。它根据信息强行切断你。

仅仅因为我们拥有await的简单异步IO,现在并不意味着您可以通过1000个并行请求来垃圾邮件。

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

答案 1 :(得分:3)

正如我们在他的回答中已经说过的那样,你的服务器正在关闭连接,导致错误信息:

An existing connection was forcibly closed by the remote host.

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

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

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

答案 2 :(得分:0)

我前段时间写过这个要点

//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个请求,但即使这是对系统/网络资源的压力测试。