HttpClient异步请求失败

问题描述 投票:1回答:2

我需要从大约3000个URL中获取内容。我使用的是 HttpClient创作 Task 对于每个url,添加任务到列表,然后 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);

然而很多任务最终都是在 FaultedCanceled 州。我想可能是具体的URL的问题,但不是。我可以用curl并行获取这些url没有问题。

我试过 HttpClientHandler, WinHttpHandler 与各种超时等。总是有几百个urls以错误告终.然后我试着以10个为一批来获取这些urls,这样就可以了。没有错误,但非常慢。Curl会以并行的方式快速获取3000个urls.然后我试着以10个为单位来获取这些urls,没有错误,但非常慢。httpbin.org 3000次,以验证问题是否与我特定的urls无关。

    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(试图并行运行多个HTTP请求,但受到Windows的限制(注册表)。)是不适用的。

另外,我需要获取的urls指向不同的主机。httpbin的代码只是一个测试,以表明问题不在于我的urls是无效的。

c# .net-core task-parallel-library dotnet-httpclient
2个回答
1
投票

正如菲尔德在评论中所说。httpClient.GetStringAsync 返回 Task. 所以,你不需要把它包起来 Task.Run.

我在控制台应用中运行了这段代码。花了50秒才完成。在你的评论中,你写道,curl在不到一分钟的时间内执行3000次查询--同样的事情。

var httpClient = new HttpClient();
var tasks = new List<Task<string>>();
var sw = Stopwatch.StartNew();

for (int i = 0; i < 3000; i++)
{
    var task = httpClient.GetStringAsync("http://httpbin.org");
    tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
sw.Stop();

Console.WriteLine(sw.Elapsed);
Console.WriteLine(tasks.All(t => t.IsCompleted));

此外,所有的请求都成功完成。

在你的代码中,你在等待任务开始时使用了 Task.Run. 但是,你需要等待完成通过调用 httpClient.Get...

© www.soinside.com 2019 - 2024. All rights reserved.