2011-04-19 11 views
8

yo pasamos mucho tiempo romper los dientes de por qué este código era 'colgando' para algunas direcciones:Cómo manejar el tiempo de espera HttpWebRequest en Fa # Async.Parallel

let getImage (imageUrl:string) = 
    async { 
     try 
      let req = WebRequest.Create(imageUrl) :?> HttpWebRequest 
      req.UserAgent <- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)"; 
      req.Method <- "GET"; 
      req.AllowAutoRedirect <- true; 
      req.MaximumAutomaticRedirections <- 4; 
      req.Timeout <- 3000; //HAHAHA, nice try! 
      let! response1 = req.AsyncGetResponse() 
      let response = response1 :?> HttpWebResponse 
      use stream = response.GetResponseStream() 
      let ms = new MemoryStream() 
      let bytesRead = ref 1 
      let buffer = Array.create 0x1000 0uy 
      while !bytesRead > 0 do 
       bytesRead := stream.Read(buffer, 0, buffer.Length) 
       ms.Write(buffer, 0, !bytesRead) 
      return SuccessfulDownload(imageUrl, ms.ToArray()) 

     with 
      ex -> return FailedDownload(imageUrl, ex.Message) 
    } 

Después de lograr localizar a cuál de los 3000 las URL estaban colgando, me enteré de que AsyncGetResponse no toma nota de HttpWebRequest.Timeout. He hecho un poco de búsqueda que arroja sugerencias para envolver la solicitud asíncrona en un hilo con un temporizador. Eso es genial para C#, pero si estoy ejecutando 3000 de estos a través de Async.Parallel |> Async.RunSynchronously, ¿cuál es la mejor manera de manejar este problema?

+1

Simplemente debe hacer ['stream.CopyTo ms'] (http://msdn.microsoft.com/en-us/library/dd782932.aspx) en lugar de toda la copia manual con' buffer' y 'bytesRead' . – ildjarn

+0

@ildjarn, gracias por la información, tengo que admitir que fue un copy-paste directo de [aquí] (http://fdatamining.blogspot.com/2010/07/f-async-workflow-application-flickr.html) – Benjol

Respuesta

7

que he probado sólo más o menos esto, pero debería tener el comportamiento correcto:

type System.Net.WebRequest with 
    member req.AsyncGetResponseWithTimeout() = 
    let impl = async { 
     let iar = req.BeginGetResponse (null, null) 
     let! success = Async.AwaitIAsyncResult (iar, req.Timeout) 
     return if success then req.EndGetResponse iar 
      else req.Abort() 
        raise (System.Net.WebException "The operation has timed out") } 
    Async.TryCancelled (impl, fun _ -> req.Abort()) 

En su código, llame req.AsyncGetResponseWithTimeout() en lugar de req.AsyncGetResponse().

+0

¡Excelente! ¡Funciona! – Benjol

+0

Hm, hablé demasiado pronto. Funciona bien para unos pocos, pero ahora estoy recibiendo un montón de tiempos de espera. ¿Es posible que WebRequest administre internamente el número de conexiones simultáneas y ahora estoy agotando las solicitudes en cola? Seguiré cavando ... – Benjol

+0

@Benjol: Sip, según recuerdo, por defecto está internamente limitado a dos conexiones simultáneas. Sin embargo, parece recordar que es bastante trivial trabajar por todos lados. Revisaré un viejo código de C# para intentar recordar cómo. – ildjarn

Cuestiones relacionadas