2012-05-30 6 views
6

Tengo una aplicación web que obtiene datos de servicios externos. La solicitud en sí sucede como el siguiente código, bastante sencillo por lo que puedo ver. Cree una solicitud, desactívela de forma asíncrona y permita que la devolución de llamada maneje la respuesta. Funciona bien en mi entorno de desarrollo.Async webrequest veces fuera => Bloqueos 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); 
      } 
     } 

Sin embargo, los entornos de prueba externos y el entorno de producción podrían, por motivos ajenos a mí, no alcanzar la URL de destino. Bien, pensé, un tiempo de espera de solicitud no dañará a nadie. Sin embargo, parecía que cada vez que se agotaba el tiempo de espera de esta solicitud, ASP.NET se bloqueaba e IIS se reiniciaba. El registro de eventos me muestra, entre otras cosas, esto 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) 

.so todo se reduce a un SocketException, me parece. Y justo después (en el mismo segundo) otro evento (que supongo que es relevante) ha iniciado la sesión:

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

Esto es algo más allá de mí como yo no soy maestro de código asíncrono y roscado, pero eso un tiempo de espera de una solicitud web hace que IIS se bloquee parece muy extraño. Reimplementé MakeRequest para realizar las solicitudes sincrónicamente, lo que funciona muy bien. La solicitud todavía expira en esos entornos, pero no se produce ningún daño y la aplicación continúa funcionando felizmente para siempre.

Así que he resuelto el problema, pero ¿por qué sucede esto? ¿Alguien puede iluminarme? :-)

Respuesta

12

Su continuación necesita controlar el hecho de que .Result podría reflejar una excepción. De lo contrario, tiene una excepción no controlada. Las excepciones no controladas matan los procesos.

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

su edad gestor de excepciones sólo cubre la creación de la tarea - no la devolución de llamada.

+0

Ah, entonces la explicación es así de simple. Pareció hacer el truco de inmediato. ¡Muchas gracias! Ahora, cómo recuperar esas horas desperdiciadas ... –

Cuestiones relacionadas