2018-08-22 异步httpclient(httpasyncclient)的使用与总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018-08-22 异步httpclient(httpasyncclient)的使用与总结相关的知识,希望对你有一定的参考价值。
参考技术A connectTimeOut:连接超时,连接建立时间,三次握手完成时间;socketTimeOut: 请求超时,传输过程中数据包之间间隔最大时间;
connectRequestTimeOut: 使用连接池管理连接,从连接池获取连接的超时时间。
ConnTotal:连接池中最大连接数;
ConnPerRoute(1000):分配给同一个route(路由)最大的并发连接数,route为运行环境机器到目标机器的一条线路,举例来说,我们使用HttpClient的实现来分别请求 www.baidu.com 的资源和 www.bing.com 的资源那么他就会产生两个route;
对于上述的实验,在一定程度上可以通过增大最大连接数来解决ConnectionRequestTimeout的问题!
HttpClient 异步请求失败
【中文标题】HttpClient 异步请求失败【英文标题】:HttpClient async requests failing 【发布时间】: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 执行此操作的出色示例。
【参考方案1】:
正如 Fildor 在 cmets 中所说,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...
启动的任务完成
【讨论】:
我不认为Task.Run
是问题所在。当与异步委托一起使用时,此方法会为创建的Task
创建一个瘦包装器(代理)。在这种情况下,它没有任何好处,但也不应该是有害的(除了会损害代码的可读性)。以上是关于2018-08-22 异步httpclient(httpasyncclient)的使用与总结的主要内容,如果未能解决你的问题,请参考以下文章