HttpClient throws TaskCanceledException on timeout

error msg:

  HttpClient throws TaskCanceledException on timeout

  HttpClient is throwing a TaskCanceledException on timeout in some circumstances. This is happening for us when the server is under heavy load. We were able to work around by increasing the default timeout. The following MSDN forum thread captures the essence of the experienced issue: 

 

error code:

var c = new HttpClient();
try
{
	c.Timeout = TimeSpan.FromMilliseconds(10);
	var x = await c.GetAsync("http://linqpad.net");	
}
catch(TaskCanceledException ex)
{
	Console.WriteLine("should not see this type of exception!");	
	Console.WriteLine(ex.Message);
}

  

Surely this should be throwing a WebException? This behaviour makes it difficult to tell the difference between timeout exceptions & legit cancellations!

 

1.

Did you ever thik about doing what I did below?

 

 

var c = new HttpClient();
try
{
 c.Timeout = TimeSpan.FromMilliseconds(10);
}
catch(TaskCanceledException ex)
{
 Console.WriteLine("should not see this type of exception!"); 
 Console.WriteLine(ex.Message);
}

try
{
 var x = await c.GetAsync("http://linqpad.net"); 
}
catch(TaskCanceledException ex)
{
 Console.WriteLine("should not see this type of exception!"); 
 Console.WriteLine(ex.Message);
}

 

 

2.

Hmm. I'm not sure if you understand the problem (or I don't understand your answer!)

I've moved the code where I set the timeout. The TaskCanceledException is thrown from the call to GetAsync.

var c = new HttpClient(); c.Timeout = TimeSpan.FromMilliseconds(10); try { var x = await c.GetAsync("http://linqpad.net"); } catch(TaskCanceledException ex) { Console.WriteLine("should not see this type of exception!?"); Console.WriteLine(ex.Message); }

 

I don't think this is a very good API. It shouldn't throw a TaskCanceledException unless the caller supplies a CancelationToken and calls Cancel! It should be a WebException.

 

 

 

3.

You code has four diffent Network layers where an error can occur

1) You first have to interface with a socket on the local computer.

2) A TCP connection has to complete from a socket with an IP address on your computer to an IP address on the server

3) On top of TCP and HTTP negotiation has to occur.  You may need credentials (cookies, certifications, login) for this to complete.

4) Then the webpage has to run which sends data back to your computer.

 

Some of the above errors will actually return error strings, other errors may cause the application to hang causing a time out.  Some errors will occur in less than 10msec.   10msec is one timer tick and the timer ticks on a computer don't run exactly at 10msec.  If you are having an error that is occuring that quickly it is probably on yhour local host.

 

Try putting the URL into a webbrowser to see what errors you get with a webbrower.  If the webbrowser works then you are mising an instruction in your code to use credentials.  See this webpage

http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/35e2a7ae-bba0-406a-a10e-f1677dd5a471

 

I'm not sure if you should be using the default credentials or using a specific proxy setting

 

 

 

4.

Hi Joel,

I'm sorry but I don't think you've understood the post. I know why the code throws an exception, it is because the HTTP request can't be serviced within the specified timeout period (if you increase the timeout the request is serviced). The post is about the type of exception that is being raised.

I'm suggesting that it shouldn't be a TaskCanceledException but a WebException!

Does that make sense?

 

 

 

5.

For me the point is that as good developers we check the msdn documentation for possible exceptions and catch only those. In this case we should actually be catching a WebException (http://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout.aspx).

I guess what James is saying is that either the documentation is wrong or there is a bug. Unless, that is, theres some other documentation/rule/recommendation somewhere that recommends we warp all async calls in a try/catch(TaskCancelledException) and I doubt there is.

 

 

 

7.

Exactly! (Thanks Stelrad)

The caller should be provided with a detailed WebException.

*update* I've update the question replaced TimeoutException -> WebException

 

 

8.

Just to ram the point home... if you wanted to support cancellation and implement an interesting WebException handling routine (for example a retry policy), you might end up with the following abomination.

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!?) } }

 

I find it hard to believe that this is by design.

 

 

 

9.

Whether the right design or not, by design OperationCanceledExceptions are thrown for timeouts (and TaskCanceledException is an OperationCanceledException).

 

 

10.

Our team found this unintuitive, but it does seem to be working as designed. Unfortunately, when we hit this, we wasted a bit of time trying to find a source of cancelation. Having to handle this scenario differently from task cancelation is pretty ugly (we created custom handler to manage this). This also seems to be an overuse of cancelation as a concept.

Thanks again for thee quick response.

 

 

11.

This is a bad design IMO. There's no way to tell if the request was actually canceled (i.e. the cancellation token passed to SendAsync was canceled) or if the timeout was elapsed. In the latter case, it would make sense to retry, whereas in the former case it wouldn't. There's a TimeoutException that would be perfect for this case, why not use it?

 

 

12.

We have several options what to do when timeout happens:

  1. Throw TimeoutException instead of TaskCanceledException.
    • Pro: Easy to distinguish timeout from explicit cancellation action at runtime
    • Con: Technical breaking change - new type of exception is thrown.
  2. Throw TaskCanceledException with inner exception as TimeoutException
    • Pro: Possible to distinguish timeout from explicit cancellation action at runtime. Compatible exception type.
    • Open question: Is it ok to throw away original TaskCanceledException stack and throw a new one, while preserving InnerException (as TimeoutException.InnerException)? Or should we preserve and just wrap the original TaskCanceledException?
      • Either: new TaskCanceledException -> new TimeoutException -> original TaskCanceledException (with original InnerException which may be null)
      • Or: new TaskCanceledException -> new TimeoutException -> original TaskCanceledException.InnerException (may be null)
  3. Throw TaskCanceledException with message mentioning timeout as the reason
    • Pro: Possible to distinguish timeout from explicit cancellation action from the message / logs
    • Con: Cannot be distinguished at runtime, it is "debug only".
    • Open question: Same as in [2] - should we wrap or replace the original TaskCanceledException (and it stack)

I am leaning towards option [2], with discarding original stack of original TaskCanceledException.
@stephentoub @davidsh any thoughts?

BTW: The change should be fairly straightforward in HttpClient.SendAsync where we set up the timeout:

CancellationTokenSource cts; 
 bool disposeCts; 
 bool hasTimeout = _timeout != s_infiniteTimeout; 
 if (hasTimeout || cancellationToken.CanBeCanceled) 
 { 
     disposeCts = true; 
     cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _pendingRequestsCts.Token); 
     if (hasTimeout) 
     { 
         cts.CancelAfter(_timeout); 
     } 
 } 
 else 
 { 
     disposeCts = false; 
     cts = _pendingRequestsCts; 
 } 
  
 // Initiate the send. 
 Task<HttpResponseMessage> sendTask; 
 try 
 { 
     sendTask = base.SendAsync(request, cts.Token); 
 } 
 catch 
 { 
     HandleFinishSendAsyncCleanup(cts, disposeCts); 
     throw; 
 } 

  

.NET Framework also throws TaskCanceledException when you set HttpClient.Timeout.

 

 

 




转载于:https://www.cnblogs.com/panpanwelcome/p/10330053.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HttpClient是一个非常常用的HTTP客户端库,可以用于发送HTTP请求并接收响应。在封装HttpClient时,我们可以考虑以下几点: 1. 连接池管理:HttpClient可以通过连接池来管理HTTP连接,从而提高性能。我们可以设置最大连接数、每个路由的最大连接数等参数,以便更好地利用连接池。 2. 超时设置:在发送HTTP请求时,我们需要设置超时时间,以避免请求过程中出现阻塞或超时等问题。可以设置连接超时时间、读取超时时间等参数。 3. 异常处理:在发送HTTP请求时,可能会出现各种异常情况,例如网络异常、连接超时等。我们需要对这些异常进行处理,以便及时发现问题并进行处理。 4. 请求头设置:在发送HTTP请求时,我们需要设置请求头,以便服务器能够正确地处理请求。可以设置User-Agent、Content-Type等参数。 5. SSL支持:如果需要发送HTTPS请求,则需要支持SSL协议。可以通过配置SSLContext来实现SSL支持。 下面是一个简单的HttpClient封装示例: ```java public class HttpClientUtil { private static final int MAX_TOTAL = 200; private static final int MAX_PER_ROUTE = 50; private static final int CONNECT_TIMEOUT = 5000; private static final int SOCKET_TIMEOUT = 10000; private static CloseableHttpClient httpClient; static { PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(MAX_TOTAL); cm.setDefaultMaxPerRoute(MAX_PER_ROUTE); RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(CONNECT_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT) .build(); httpClient = HttpClients.custom() .setConnectionManager(cm) .setDefaultRequestConfig(requestConfig) .build(); } public static String doGet(String url, Map<String, String> headers) throws IOException { HttpGet httpGet = new HttpGet(url); if (headers != null) { for (Map.Entry<String, String> entry : headers.entrySet()) { httpGet.setHeader(entry.getKey(), entry.getValue()); } } try (CloseableHttpResponse response = httpClient.execute(httpGet)) { HttpEntity entity = response.getEntity(); if (entity != null) { return EntityUtils.toString(entity); } } return null; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值