Ustawienie HttpClient na zbyt krótki timeout powoduje awarię procesu
Zauważyłem, że kiedy używam System.Net.HttpClient
z krótkim czasem oczekiwania, może to czasami spowodować awarię procesu, nawet jeśli jest on zawinięty w blok try-catch. Oto krótki program do odtworzenia tego.
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);
}
}
Uruchomienie tego spowoduje awarię procesu z następującym wyjątkiem:
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()
Grzebanie w trochę, wydaje się, że gdy HttpClient
przerywa żądanie przed utworzeniem odpowiedniego ServicePoint
, HttpWebRequest
próbuje utworzyć ServicePoint
, poprzez ServicePointManager.FindServicePoint
, który rzuca RequestCanceled. Ponieważ ten wyjątek jest wrzucany do wątku, który próbuje anulować żądanie, nie jest przechwytywany, a proces umiera.
Czy coś przeoczyłem? Spotkałeś się z tym problemem?
2 answers
HttpWebRequest.Abort()
rzuca wyjątek na wątek tła/timera. Nie ma to nic wspólnego z zarządzaniem zadaniami HttpClient.
Wyjątek od HttpWebRequest.Abort()
powinien być naprawiony w. NET 4.5 GDR1.
http://support.microsoft.com/kb/2750149
http://support.microsoft.com/kb/2750147
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-18 17:45:05
Wygląda na to, że jest to jakiś błąd w tym, jak asynchroniczna obsługa HttpClient zarządza zadaniami. Udało mi się uruchomić elementy równolegle, ale uruchomić je synchronicznie i działa. Nie jestem pewien, czy chcesz po prostu zapobiec nieobsługiwanym błędom, czy nie. To uruchamiało równoległe zadania, ale nie są one asynchroniczne, odkąd go wyłączyłem. Na moim komputerze zawsze dostaję 5 rund i się rozbija. Nawet jeśli ustawię to na timeout po sekundzie, to tak jakby awarie w wątkach nadal wybuchały w górę, gdyby były asynchroniczne.
Myślę, że to błąd, nie wyobrażam sobie, żeby to było zamierzone zachowanie.
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();
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-18 17:24:12