2011-04-19 12 views
8

abbiamo appena trascorso un lungo periodo di rottura i denti sul motivo per cui questo codice era 'appesa' per alcuni URL:Come gestire HttpWebRequest timeout in F # 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) 
    } 

Dopo essere riuscito a rintracciare quale dei 3000 url era appeso, ho appreso che AsyncGetResponse non presta alcuna notifica di HttpWebRequest.Timeout. Ho fatto un po 'di ricerca che suggerisce suggerimenti di avvolgere la richiesta asincrona in un thread con un timer. Questo è ottimo per C#, ma se sto eseguendo 3000 di questi tramite Async.Parallel |> Async.RunSynchronously, qual è il modo migliore per gestire questo problema?

+1

Dovresti semplicemente eseguire ['stream.CopyTo ms'] (http://msdn.microsoft.com/en-us/library/dd782932.aspx) piuttosto che copiare manualmente con' buffer' e 'bytesRead' . – ildjarn

+0

@ildjarn, grazie per le informazioni, devo ammettere che è stato un copia-incolla diretto da [qui] (http://fdatamining.blogspot.com/2010/07/f-async-workflow-application-flickr.html) – Benjol

risposta

7

ho solo approssimativamente testato questo, ma dovrebbe avere il comportamento corretto:

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()) 

Nel codice, chiamare req.AsyncGetResponseWithTimeout() invece di req.AsyncGetResponse().

+0

Eccellente! Funziona! – Benjol

+0

Hm, ho parlato troppo presto. Funziona bene per pochi, ma ora sto diventando un gran numero di timeout. È possibile che WebRequest gestisca internamente il numero di connessioni simultanee e ora sto scadenzando le richieste in coda? Continuerò a scavare ... – Benjol

+0

@ Benjol: Sì, come ricordo, per impostazione predefinita è limitato internamente a due connessioni simultanee. Mi sembra di ricordare che essere piuttosto semplice da risolvere però. Guarderò attraverso un vecchio codice C# per cercare di ricordare come. – ildjarn

Problemi correlati