2012-05-30 9 views
6

Ho un'app Web che riceve dati da servizi esterni. La richiesta stessa avviene come il seguente codice - abbastanza semplice per quanto posso vedere. Crea una richiesta, sparagli via in modo asincrono e lascia che il callback gestisca la risposta. Funziona benissimo sul mio ambiente di sviluppo.Timeout richiesta asincrona => Arresti anomali IIS

public static void MakeRequest(Uri uri, Action<Stream> responseCallback) 
     { 
      WebRequest request = WebRequest.Create(uri); 
      request.Proxy = null; 
      request.Timeout = 8000; 
      try 
      { 
       Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null) 
        .ContinueWith(task => 
             { 
              WebResponse response = task.Result; 
              Stream responseStream = response.GetResponseStream(); 
              responseCallback(response.GetResponseStream()); 
              responseStream.Close(); 
              response.Close(); 
             }); 
      } catch (Exception ex) 
      { 
       _log.Error("MakeRequest to " + uri + " went wrong.", ex); 
      } 
     } 

Tuttavia ambienti di test esterni e l'ambiente di produzione potrebbe, per ragioni indipendenti me, non raggiungono l'URL di destinazione. Bene, ho pensato - un timeout della richiesta non farà davvero male a nessuno. Tuttavia, sembrava che ogni volta che questa richiesta fosse scaduta, ASP.NET si arrestava in modo anomalo e IIS veniva riavviato. Il registro eventi mi mostra, tra le altre cose, questo stacktrace:

StackTrace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) 
    at System.Threading.Tasks.Task`1.get_Result() 
    at MyApp.AsyncWebClient.<>c__DisplayClass2.<MakeRequest>b__0(Task`1 task) 
    at System.Threading.Tasks.Task`1.<>c__DisplayClass17.<ContinueWith>b__16(Object obj) 
    at System.Threading.Tasks.Task.InnerInvoke() 
    at System.Threading.Tasks.Task.Execute() 

InnerException: System.Net.WebException 

Message: Unable to connect to the remote server 

StackTrace: at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) 
    at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endMethod, TaskCompletionSource`1 tcs) 

InnerException: System.Net.Sockets.SocketException 

Message: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 

StackTrace: at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) 
    at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Int32 timeout, Exception& exception) 

..quindi tutto si riduce ad una SocketException, mi pare. E subito dopo (all'interno dello stesso secondo) un altro evento (che sto cercando di indovinare che è rilevante) si registra:

Exception Info: System.AggregateException 
Stack: 
    at System.Threading.Tasks.TaskExceptionHolder.Finalize() 

Questa è una sorta di là di me, come io non sono padrone di codice asincrono e filettatura, ma che un timeout da una richiesta web causa il crash di IIS sembra molto strano. Ho reimplementato MakeRequest per eseguire le richieste in modo sincrono, il che funziona alla grande. La richiesta è ancora scaduta in quegli ambienti, ma nessun danno è stato fatto e l'app continua a funzionare felicemente per sempre.

Quindi ho risolto il problema, ma perché succede? Qualcuno può illuminarmi? :-)

risposta

12

La tua continuazione deve gestire il fatto che .Result potrebbe riflettere un'eccezione. Altrimenti hai un'eccezione non gestita. Le eccezioni non gestite eliminano i processi.

.ContinueWith(task => 
{ 
    try { 
     WebResponse response = task.Result; 
     Stream responseStream = response.GetResponseStream(); 
     responseCallback(responseStream); 
     responseStream.Close(); 
     response.Close(); 
    } catch(Exception ex) { 
     // TODO: log ex, etc 
    } 
}); 

tua vecchio gestore di eccezioni copre solo il creazione del compito - non il callback.

+0

Ah, quindi la spiegazione è così semplice. Sembrava fare il trucco immediatamente. Grande grazie! Ora, come ottenere indietro quelle ore sprecate ... –