Avatar billede speedpete Nybegynder
06. februar 2012 - 08:38 Der er 5 kommentarer og
1 løsning

Timeout-problem ved læsnig af http fra aspx

Jeg har i min .aspx følgende metode:

private static string GetPageAsString(string address)
        {
            string result = "";

            WebRequest request = WebRequest.Create(address);
            request.Timeout = 2000;

            using (WebResponse response = request.GetResponse())
            {
                StreamReader reader = new StreamReader(response.GetResponseStream());
                result = reader.ReadToEnd();
            }
            return result;
        }

Den bruger jeg til at læse en webside, som returnerer noget xml, som jeg parser (det er noget vejrdata; en 'address' repræsenterer en by, og jeg har mange byer i min web-app).

Problemet er, at hvis serveren som leverer data til mig ikke svarer (hvilket hænder fra tid til anden), så har request.Timeout INGEN effekt: Kodafviklingen stopper i MANGE sekunder, og så hænger hele min web-app.

Spørgsmål: What to do? Er der et nemt fix? Eller kan jeg evt. lave noget asynkront halløj, og i givet fald, hvordan skruer jeg det sammen? Det må meget gerne fungere på samme måde som metoden ovenfor: Returnere en streng efter en max-ventetid, og hvis ventetiden er overskredet, så bare kaste en exception.
Avatar billede jokkejensen Novice
06. februar 2012 - 09:37 #1
Vejr data skal vel næppe søgemaskine indekseres, så brugt et asynkront kald vha javascript til tjenesten.

Og behandl evt fejl der samtidigt.

/J
Avatar billede speedpete Nybegynder
06. februar 2012 - 09:53 #2
Duer ikke.  Dels skal jeg sende en nøgle med i requesten, dels skal jeg cache data for ikke at overbelaste tjenestens servere.

Så serverside er nødvendigt.
Avatar billede arne_v Ekspert
06. februar 2012 - 23:11 #3
app (f.eks. Windows service) som henter data hver X sekund og gemmer dem hvorefter din web app bare server det som static content!?!?
Avatar billede speedpete Nybegynder
07. februar 2012 - 09:16 #4
arne_v, det var måske en ide. Jeg har i mellemtiden på et konceptuelt plan lavet noget med en klasse, som asynkront starter et request og gemmer resultatet i sig selv som key/value. Så kan jeg måske styre time-out ved føsrt at kalde dennne klasses beginrequest, og så prøve i x antal sekunder at hente resultatet ud, og så give op hvis det ikke lykkes.

Men ellers er dit svar det bedste jeg har fået indtil videre.
Avatar billede speedpete Nybegynder
08. februar 2012 - 13:49 #5
Nu har jeg så bikset mig frem til følgende, og modtager gerne kommentarer på, om jeg har overset noget farligt (memoryleak, performance, skal jeg bruge EndInvoke, bør jeg låse min Dictionary retrievedxml ....)

Den her metode beder min AsyncWebRequester-klasse (se nedenfor) om at starte et webrequest (StartRequest(address)). Dernæst beder den igen om et svar (GetResult(address)), men venter max 10ms på et svar.

private static string GetPageAsString(string address)
        {
            AsyncWebRequester.StartRequest(address);
            TimeSpan timeout = new TimeSpan(1000000);  // 1 second=10,000,000         
            DateTime tryToGetAnswerUntil = DateTime.Now.Add(timeout);
            string returnXML = "";
            while (string.IsNullOrEmpty(returnXML) && DateTime.Now < tryToGetAnswerUntil )
            {
                returnXML = AsyncWebRequester.GetResult(address);               
            }
            return returnXML;
        }

Og så min AsyncWebRequester-klasse. Filosofien er, at jeg starter et webrequest, og enten får jeg hurtigt svar, og så er det fint - eller også får jeg ikke hurtigt svar, og så er jeg ligeglad, fordi så får jeg sandsnyligvis aldrig noget svar:

public class AsyncWebRequester
    {
        private static Dictionary<string, string> retrievedxml = new Dictionary<string, string>();

        public static void StartRequest(string uri)
        {
            try
            {
                InitiateAsyncRequestDelegate mydelegate = new InitiateAsyncRequestDelegate(InitiateAsyncRequest);
                mydelegate.BeginInvoke(uri, null, null);
            }
            catch {}
        }     

        private delegate void InitiateAsyncRequestDelegate(string uri);

        private static void InitiateAsyncRequest(string uri)
        {
            WebRequest request;
            try
            {
                request = WebRequest.Create(uri);             

                using (WebResponse response = request.GetResponse())
                {
                    StreamReader reader = new StreamReader(response.GetResponseStream());
                    string result = reader.ReadToEnd();
                    retrievedxml.Add(uri, result);
                }
            }
            catch { }
            finally { request = null; }
        }

        public static string GetResult(string uri)
        {
            string xml = "";
            if (retrievedxml.ContainsKey(uri))
            {           
                xml = retrievedxml[uri];
                retrievedxml.Remove(uri);
            }
            return xml;             
        }

    }
Avatar billede speedpete Nybegynder
13. februar 2012 - 14:49 #6
Jeg har fundet noget på http://www.yoda.arachsys.com/csharp/threads/threadpool.shtml, hvor jeg bruger ThreadUtil-klassen som følger:

public static void StartRequest(string uri)
        {
            try
            {
                InitiateAsyncRequestDelegate mydelegate = new InitiateAsyncRequestDelegate(InitiateAsyncRequest);               
                ThreadUtil.FireAndForget(mydelegate, uri);
            }
            catch {}
        } 


ThreadUtil wrapper en delegate i en anden delegate, og kalder EndInvoke().
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester