【问题标题】:HttpClient (C#) fails on many asynchronous requests?HttpClient (C#) 在许多异步请求上失败?
【发布时间】:2014-02-04 16:36:10
【问题描述】:

我正在使用 HttpClient 异步向外部 api 发出许多请求。我等待所有请求完成,然后在其他代码中使用响应。我的问题是,如果我发出太多请求,当我使用 Task.WhenAll 等待时,我的代码会抛出异常。

此代码最终将并行运行,我的意思是我将同时执行多组这些异步请求(即 10 组 200 个异步请求)。我已经实例化了一个与 .NET 4.5 async/await 修饰符一起使用的 HttpClient,如下所示:

using (var client = new HttpClient())
{
    // make a list of tasks
    List<Task<HttpResponseMessage>> taskList;
    List<MyData> replies;
    for (int i = 0; i < MAX_NUMBER_REQUESTS; i++)
    {
        taskList.Add(client.GetAsync(externalUri);
    }

    List<HttpResponseMessage> responses = await Task.WhenAll(taskList);

    // read each response after they have returned
    foreach (var response in responses)
    {
        var reader = new System.IO.StreamReader(await response.Content.ReadAsStreamAsync());
        replies.Add(JsonConvert.DeserializeObject<MyData>(reader.ReadToEnd()));
        reader.Close();
} 

    foreach (var reply in replies)
    {
        // do something with the response from the external api call...
    }
}

我不断收到 TaskCanceledException。在调查了这个之后,我发现这可能是一个超时问题。我不知道如何解决它。在使用 Task.WhenAll 并重复之前,我尝试将我的请求批处理为 100 个请求,这很有效。然后,当我与三组 100 个失败的请求并行运行时。我错过了什么吗,有人对此有任何见解吗?

【问题讨论】:

  • 你是如何并行运行这三组的?
  • 一个计时器被触发以开始整个过程​​,然后它在我们数据集中的每个对象上调用一个计时器。然后,我们数据集中的每个对象都会调用此函数,以通过 api 调用检索其数据。如果那还不够清楚,我可以提供伪代码。
  • 当您需要限制并发请求时,TPL 数据流与 HttpClient 配合得很好。一次启动数百个请求可能不是一个好主意。

标签: c# asynchronous .net-4.5 dotnet-httpclient async-await


【解决方案1】:

调整ServicePointManager.DefaultConnectionLimit。对于大量并发请求,您可以将其设置为int.MaxValue

【讨论】:

【解决方案2】:

TaskCanceledException 可能是由于您的HttpClient 在您的相关请求完成之前被处置。我怀疑给您异常的代码与您发布的示例不同,因为它不会编译?

对于最多 2000 个请求,以下内容对我来说很好:

using (var client = new HttpClient())
{
    List<Task<HttpResponseMessage>> taskList = new List<Task<HttpResponseMessage>>();
    List<MyData> replies = new List<MyData>();
    for (var i = 0; i < MAX_NUMBER_REQUESTS; ++i)
    {
        taskList.Add(client.GetAsync(externalUrl));
    }
    var responses = await Task.WhenAll(taskList);

    foreach (var m in responses)
    {
        using (var reader = new StreamReader(await m.Content.ReadAsStreamAsync()))
        {
            replies.Add(JsonConvert.DeserializeObject<MyData>(reader.ReadToEnd()));
        }
    }

    foreach (var reply in replies)
    {
        // TODO:        
    }
}

因此,您可能还遇到了一些其他问题,即您没有提供足够的细节让任何人都知道。

除了我使用Parallel.ForEach 评论过的问题之外:ForEach 被阻止,因此您的 UI 将被阻止。

【讨论】:

  • 现在假设您在网络爬虫中使用此代码,并且您想从不同站点下载数百万个 url,您认为一次启动数百万个任务是否合乎逻辑。 (我喜欢这个。评论某人的答案比写一个更容易:))
  • 这不是重点,重点是代码按描述工作并且没有显示他详细说明的错误。为了“解决”他的问题,我们需要更多信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-30
相关资源
最近更新 更多