【发布时间】:2020-05-01 17:40:20
【问题描述】:
我需要从大约 3000 个网址中获取内容。我正在使用HttpClient,为每个网址创建Task,将任务添加到列表,然后await Task.WhenAll。像这样的
var tasks = new List<Task<string>>();
foreach (var url in urls) {
var task = Task.Run(() => httpClient.GetStringAsync(url));
tasks.Add(task);
}
var t = Task.WhenAll(tasks);
但是,许多任务最终都处于Faulted 或Canceled 状态。我认为具体的网址可能有问题,但没有。我可以使用 curl 并行获取这些 url。
我尝试了HttpClientHandler、WinHttpHandler 和各种超时等。总是有几百个网址以错误结尾。
然后我尝试以 10 个为一组获取这些 url,这很有效。没有错误,但很慢。 Curl 将非常快地并行获取 3000 个 url。
然后我尝试获取httpbin.org 3000 次以验证问题不在于我的特定网址:
var handler = new HttpClientHandler() { MaxConnectionsPerServer = 5000 };
var httpClient = new HttpClient(handler);
var tasks = new List<Task<HttpResponseMessage>>();
foreach (var _ in Enumerable.Range(1, 3000)) {
var task = Task.Run(() => httpClient.GetAsync("http://httpbin.org"));
tasks.Add(task);
}
var t = Task.WhenAll(tasks);
try { await t.ConfigureAwait(false); } catch { }
int ok = 0, faulted = 0, cancelled = 0;
foreach (var task in tasks) {
switch (task.Status) {
case TaskStatus.RanToCompletion: ok++; break;
case TaskStatus.Faulted: faulted++; break;
case TaskStatus.Canceled: cancelled++; break;
}
}
Console.WriteLine($"RanToCompletion: {ok} Faulted: {faulted} Canceled: {cancelled}");
同样,数百个任务总是以错误结束。
那么,这里的问题是什么?为什么我无法使用async 获取这些网址?
我正在使用 .NET Core,因此使用 ServicePointManager (Trying to run multiple HTTP requests in parallel, but being limited by Windows (registry)) 的建议不适用。
另外,我需要获取的 url 指向不同的主机。 httpbin 的代码只是一个测试,表明问题不在于我的 url 无效。
【问题讨论】:
-
你为什么将
httpClient.GetStringAsync(url)包裹在Task.Run中?它已经给了你一个Task<string>。此外,几乎同时启动 所有 这些请求,我实际上预计有些会出现故障/超时。我会尝试使用Parallel.ForEach来更好地控制并行性。 -
我猜
Task.WhenAll()在任何任务抛出异常时都会失败。尝试将httpClient.GetAsync()包装在 try 块中 -
另请注意,一次用 3000 个请求轰炸一个 url(或至少 非常 短间隔)可以让你被洪水禁止或节流最少。
-
附带说明一下,用 3000 个请求轰炸 httpbin.org,并发布邀请每个人都这样做的代码,可能会导致糟糕站点的托管成本增加,并且可以被视为DDoS attack 的温和形式。所以我个人不会尝试验证 OP 的实验。
-
在“真实”场景中,3000 个 URL 是否命中同一主机?如果是(或者即使不是),同时发起 3000 个请求是不可取的。考虑一次限制为 50 或 100 个。 Here 您会发现使用 SemphorSlim 和 TPL Dataflow 执行此操作的出色示例。
标签: c# .net-core task-parallel-library dotnet-httpclient