如何判断 HttpClient 何时超时?

Posted

技术标签:

【中文标题】如何判断 HttpClient 何时超时?【英文标题】:How can I tell when HttpClient has timed out? 【发布时间】:2012-05-19 20:17:52 【问题描述】:

据我所知,没有办法知道具体是发生了超时。我没有找对地方,还是我错过了更大的东西?

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
;
try

    var s = client.GetAsync("").Result;

catch(Exception e)

    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);

这会返回:

发生了一个或多个错误。

一个任务被取消了。

【问题讨论】:

我们可以在 GitHub 上为这个问题投票:HttpClient throws TaskCanceledException on timeout #20296 对这个问题投了极大的赞成票。另外...知道如何在 UWP 上执行此操作吗?它的 Windows.Web.HTTP.HTTPClient 没有超时成员。 GetAsync 方法也不接受取消令牌... 6 年后,似乎仍然无法知道客户端是否超时。 .NET 5 终于实现了一个包装好的TimeoutException;在下面查看我的答案以获取示例实现。 【参考方案1】:

我正在重现同样的问题,这真的很烦人。我发现这些很有用:

HttpClient - dealing with aggregate exceptions

Bug in HttpClient.GetAsync should throw WebException, not TaskCanceledException

一些代码以防链接不通:

var c = new HttpClient();
c.Timeout = TimeSpan.FromMilliseconds(10);
var cts = new CancellationTokenSource();
try

    var x = await c.GetAsync("http://linqpad.net", cts.Token);  

catch(WebException ex)

    // handle web exception

catch(TaskCanceledException ex)

    if(ex.CancellationToken == cts.Token)
    
        // a real cancellation, triggered by the caller
    
    else
    
        // a web request timeout (possibly other things!?)
    

【讨论】:

根据我的经验,在任何情况下都无法捕获 WebException。其他人是否正在经历不同的事情? @crush WebException 可以被抓到。也许this 会有所帮助。 如果我不使用 cts,这对我不起作用。我只是在使用 Task task = SomeTask() try T result = task.Result catch (TaskCanceledException) catch (Exception e) 只捕获一般异常,而不是 TaskCanceledException。我的代码版本有什么问题? 我创建了一个新的错误报告,因为原来的错误报告似乎在存档的论坛帖子中:connect.microsoft.com/VisualStudio/feedback/details/3141135 如果令牌是从外部传递的,在与ex.CancellationToken比较之前检查它不是default(CancellationToken)【参考方案2】:

您需要等待GetAsync 方法。如果超时,它将抛出TaskCanceledException。此外,GetStringAsyncGetStreamAsync 在内部处理超时,因此它们永远不会抛出。

string baseAddress = "http://localhost:8080/";
var client = new HttpClient() 
 
    BaseAddress = new Uri(baseAddress), 
    Timeout = TimeSpan.FromMilliseconds(1) 
;
try

    var s = await client.GetAsync();

catch(Exception e)

    Console.WriteLine(e.Message);
    Console.WriteLine(e.InnerException.Message);

【讨论】:

我对此进行了测试,GetStreamAsync 为我抛出了 TaskCanceledException 如何判断TaskCanceledException 是由HTTP 超时引起的,而不是直接取消或其他原因? @UserControl 检查TaskCanceledException.CancellationToken.IsCancellationRequested。如果为 false,您可以合理地确定这是超时。 事实证明,你不能指望IsCancellationRequested 在直接取消时设置异常标记,正如我之前所想的那样:***.com/q/29319086/62600 @testing 他们没有表现不同。只是您有一个表示用户取消请求的令牌和一个表示客户端超时的内部(您无法访问且不需要)。不同的是用例【参考方案3】:

从 .NET 5 开始,the implementation has changed。 HttpClient 仍然抛出 TaskCanceledException,但现在将 TimeoutException 包装为 InnerException。因此您可以轻松检查请求是否被取消或超时(从链接的博客文章复制的代码示例):

try

    using var response = await _client.GetAsync("http://localhost:5001/sleepFor?seconds=100");

// Filter by InnerException.
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)

    // Handle timeout.
    Console.WriteLine("Timed out: "+ ex.Message);

catch (TaskCanceledException ex)

    // Handle cancellation.
    Console.WriteLine("Canceled: " + ex.Message);   

【讨论】:

这很好用,并且在通过实现@Thomas Levesque 提出的自定义 HttpHandler 来处理此问题时将所需的开销放在一边。只要您不能使用 .NET 5 或更高版本,这仍然可以。 我使用来自System.Net.Http.Json 的*JsonAsync 方法,它们在超时时抛出System.Net.Http.HttpRequestException 和内部System.Net.Sockets.SocketException【参考方案4】:

我发现确定服务调用是否超时的最佳方法是使用取消令牌而不是 HttpClient 的超时属性:

var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);

然后在服务调用过程中处理CancellationException...

catch(TaskCanceledException)

    if(cts.Token.IsCancellationRequested)
    
        // Timed Out
    
    else
    
        // Cancelled for some other reason
    

当然,如果超时发生在服务端,应该可以通过 WebException 处理。

【讨论】:

嗯,我猜应该删除否定运算符(在编辑中添加)以使该示例有意义?如果cts.Token.IsCancellationRequestedtrue 一定意味着发生了超时?【参考方案5】:

基本上,您需要捕获OperationCanceledException 并检查传递给SendAsync(或GetAsync,或您正在使用的任何HttpClient 方法)的取消令牌的状态:

如果被取消(IsCancellationRequested为真),则表示请求确实被取消了 如果没有,则表示请求超时

当然,这不是很方便……如果超时,最好接收TimeoutException。我在这里提出一个基于自定义 HTTP 消息处理程序的解决方案:Better timeout handling with HttpClient

【讨论】:

啊!是你!我今天早些时候在你的博文中写了一条评论。但是对于这个答案,我认为你关于 IsCancellationRequested 的观点是不正确的,因为当我自己没有取消它时,它似乎对我来说总是正确的 @knocte 这很奇怪......但在这种情况下,我的博客文章中的解决方案对您没有帮助,因为它也依赖于此 在关于这个问题的 github 问题中,许多人声称我说的是:IsCancellationRequested 在超时时为真;所以我很想对你的回答投反对票;) @knocte,我不知道该告诉你什么......我已经使用了很长时间,它总是对我有用。您是否将HttpClient.Timeout 设置为无穷大? @knocte 检查 Knelis 答案(2021 年 2 月 1 日),.NET 5 终于实现了一个包装好的 TimeoutException【参考方案6】:

来自http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx

域名系统 (DNS) 查询最多可能需要 15 秒才能返回或超时。如果您的请求包含需要解析的主机名,并且您将 Timeout 设置为小于 15 秒的值,则可能需要 15 秒或更长时间才能引发 WebException 以指示您的请求超时

然后您可以访问Status 属性,请参阅WebExceptionStatus

【讨论】:

嗯,我正在返回一个AggregateException,里面有一个TaskCancelledException。我一定是做错了什么…… 你在使用catch(WebException e)吗? 不,如果我尝试,AggregateException 未处理。如果你创建一个 VS 控制台项目,添加对System.Net.Http 的引用并将代码放入main,你可以自己查看(如果你愿意的话)。 如果等待时间超过了任务的超时时间,你会得到一个TaskCanceledException。这似乎是由 TPL 的内部超时处理引发的,其级别高于 HttpWebClient。有doesn't seem to be a good way 来区分超时取消和用户取消。这样做的结果是,您可能无法在 AggregateException 中获得 WebException 正如其他人所说,您必须假设 TaskCanceledException 是超时。我在这里使用 try //Code catch (AggregateException exception) if (exception.InnerExceptions.OfType().Any()) //Handle timeout here 【参考方案7】:
_httpClient = new HttpClient(handler) Timeout = TimeSpan.FromSeconds(5);

这是我通常做的,对我来说似乎很不错,使用代理时尤其好。

【讨论】:

这是设置 httpclient 超时的方法。这并没有解决问题,即如何判断 httpclient 何时超时。

以上是关于如何判断 HttpClient 何时超时?的主要内容,如果未能解决你的问题,请参考以下文章

HttpClient连接池的连接保持超时和失效机制

HttpClient连接池的连接保持超时和失效机制

HttpClient连接池

无法在 JavaScript 中为 Windows.Web.Http.HttpClient 添加超时

新手求助,Arduino联网后,如何回应HttpClient的GET请求

HttpClient连接池耗尽引发雪崩问题