将 HttpClient 设置为太短的超时会导致进程崩溃
Posted
技术标签:
【中文标题】将 HttpClient 设置为太短的超时会导致进程崩溃【英文标题】:Setting HttpClient to a too short timeout crashes process 【发布时间】:2013-03-12 14:10:14 【问题描述】:我注意到,当我使用 System.Net.HttpClient
并有短暂的超时时,有时它可能会导致进程崩溃,即使它被包装在 try-catch 块中也是如此。这是一个重现这一点的简短程序。
public static void Main(string[] args)
var tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
tasks.Add(MakeHttpClientRequest());
Task.WaitAll(tasks.ToArray());
private async static Task MakeHttpClientRequest()
var httpClient = new HttpClient Timeout = TimeSpan.FromMilliseconds(1) ;
var request = "whatever";
try
HttpResponseMessage result =
await httpClient.PostAsync("http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=766c0ac7802d55314fa980727f747710",
new StringContent(request));
await result.Content.ReadAsStringAsync();
catch (Exception x)
Console.WriteLine("Error occurred but it is swallowed: " + x);
运行此程序将使进程崩溃,并出现以下异常:
Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.Net.WebException: The request was canceled
at System.Net.ServicePointManager.FindServicePoint(Uri address, IWebProxy proxy, ProxyChain& chain, HttpAbortDelegate& abortDelegate, Int32& abortState)
at System.Net.HttpWebRequest.FindServicePoint(Boolean forceFind)
at System.Net.HttpWebRequest.get_ServicePoint()
at System.Net.AuthenticationState.PrepareState(HttpWebRequest httpWebRequest)
at System.Net.AuthenticationState.ClearSession(HttpWebRequest httpWebRequest)
at System.Net.HttpWebRequest.ClearAuthenticatedConnectionResources()
at System.Net.HttpWebRequest.Abort(Exception exception, Int32 abortState)
at System.Net.HttpWebRequest.Abort()
at System.Net.Http.HttpClientHandler.OnCancel(Object state)
at System.Threading.CancellationCallbackInfo.ExecutionContextCallback(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.CancellationCallbackInfo.ExecuteCallback()
at System.Threading.CancellationTokenSource.CancellationCallbackCoreWork(CancellationCallbackCoreWorkArguments args)
at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
--- End of inner exception stack trace ---
at System.Threading.CancellationTokenSource.ExecuteCallbackHandlers(Boolean throwOnFirstException)
at System.Threading.CancellationTokenSource.NotifyCancellation(Boolean throwOnFirstException)
at System.Threading.CancellationTokenSource.TimerCallbackLogic(Object obj)
at System.Threading.TimerQueueTimer.CallCallbackInContext(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.TimerQueueTimer.CallCallback()
at System.Threading.TimerQueueTimer.Fire()
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.TimerQueue.AppDomainTimerCallback()
稍微深入一点,似乎当HttpClient
在创建相关的ServicePoint
之前中止请求时,HttpWebRequest
会尝试通过ServicePointManager.FindServicePoint
创建ServicePoint
,这会引发 RequestCanceled。由于这个异常是在试图取消请求的线程中抛出的,所以没有被捕获,进程就死掉了。
我错过了什么吗?你遇到过这个问题吗?
【问题讨论】:
这是一个惊人的错误。 a) 你能尝试在整个 Main 周围添加一个 try-catch 吗? b) 你能钩住TaskScheduler.UnobservedTaskException 事件吗? @usr:两者都试过了,正如预期的那样,它没有帮助。 main 上的 try-catch 只捕获主线程的东西,进程崩溃异常不是 UnobservedTaskException。 你不能捕捉到聚合异常吗?并从那里处理它?捕捉(AggregateException ae) @PaulG:我不能,它是从另一个我无法控制的线程抛出的。 我认为这个问题也可能发生在更大的超时时间 【参考方案1】:HttpWebRequest.Abort()
在后台/计时器线程上引发异常。这与HttpClient的Task管理无关。
HttpWebRequest.Abort()
的异常应在 .NET 4.5 GDR1 中修复。
http://support.microsoft.com/kb/2750149
http://support.microsoft.com/kb/2750147
【讨论】:
绝对是补丁问题。我可以确认这在我的最新修补过的 Windows 8 修补笔记本电脑上可以正常工作,但在我的工作笔记本电脑上无法正常工作,因为它接收了来自公司更新的补丁。 如果您无法安装更新,有什么选择吗? (也许回到旧的 HttpWebRequest ?)【参考方案2】:看起来这是 HttpClient 的异步处理程序如何管理任务的某种错误。我能够并行启动这些项目,但同步运行它们并且它可以工作。我不确定您是否只想防止未处理的错误。这运行了并行任务,但是自从我将其关闭后,它们实际上并不是异步的。在我的电脑上,我总是打到 5 轮,然后它就会崩溃。即使我在一秒钟后将其设置为超时,如果线程中的崩溃是异步的,它们仍然会爆炸。
我认为这是一个错误,我无法想象这是预期的行为。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
namespace TestCrash
class Program
static void Main(string[] args)
try
Parallel.ForEach(Enumerable.Range(1, 1000).ToList(), i =>
Console.WriteLine(i);
using (var c = new HttpClient Timeout = TimeSpan.FromMilliseconds(1) )
var t = c.GetAsync("http://microsoft.com");
t.RunSynchronously(); //<--comment this line and it crashes
Console.WriteLine(t.Result);
);
catch (Exception x)
Console.WriteLine(x.Message);
Console.ReadKey();
【讨论】:
似乎 HttpWebRequest 中存在错误(HttpClient 在其发送中使用该错误)。请参阅 Tratcher 的回答。 不应该真正将 Parallel.ForEach 与异步一起使用。这里是怪物。以上是关于将 HttpClient 设置为太短的超时会导致进程崩溃的主要内容,如果未能解决你的问题,请参考以下文章
Apache HttpClient 4.3 - 设置连接空闲超时