Iteam
GitHub

Skapa direct-push kanal med hjälp av AjaxPro och ASP.NET

Nu bjuder vi på resultatet av en lång utredning:

Ett allt vanligare önskemål i webb 2.0-projekt är låta gränssnittet vara "live", t.ex. att det syns direkt på din skärm utan omladdning när nya besökare kommer till din sida. För att åstadkomma detta skapar man en kanal mellan server och klient med hjälp av Ajax. Detta är ett extremt effektivt sätt att låta ditt användargränssnitt vara live-kopplat mot t.ex. en databas. Det finns dock några stora utmaningar och många fallgropar att tänka på när du designar din applikation.

I teorin går det till så här:

I javascript skapar du ett asynkront event-anrop till din server:

function startListner()
{
    /* restartListner anropas vid anslutningsfel 
och startar om anslutningen */
    Site.Ajax.WaitForUpdates(gotUpdate, restartListner);
}

function restartListner()
{
    /* vänta en sekund för att låta köade 
anslutningar komma före */
    window.setTimeout(startListner, 1000); 
}

function gotUpdate(result)
{
       // hantera ditt event här
       restartListner();
}


På servern låser du din tråd och väntar på att något händer:

static Hashtable waitHandles = new Hashtable();

[AjaxPro.AjaxMethod(AjaxPro.HttpSessionStateRequirement.Read)]
public WaitResult WaitForUpdates()

    WaitResult w = new WaitResult();
          
    AutoResetEvent anEvent = new AutoResetEvent(false);
    lock (waitHandles)
    {
        waitHandles[System.Threading.Thread.CurrentThread]
= anEvent;
    }
    /* Vänta till dess att något har hänt på servern,
dock längst 5 sekunder */
    anEvent.WaitOne(new TimeSpan(0, 0, 0, 5), false);
/* Hämta alla sparade events */
    UpdateEvent[] events = frontend.Manager.GetEvents();

    w.UpdateEvents = events;
    return w;
}


När något viktigt hander på servern, t ex att något ändras i din databas så kan du skicka ett event tillbaka hela vägen till klienten genom att släppa låset och låta ovanstående request gå igenom:

// Släpp sedan alla eventlyssnare som väntar på events

lock (waitHandles)
{
    // släpp alla väntande eventlyssnare
    foreach (AutoResetEvent anEvent in waitHandles.Values)
    {
        anEvent.Set();
    }
}


Viktigt att tänka på:


  • Se till att inte ställa fler än två anrop till servern under tiden som du väntar på svar. Enligt rekommendation i HTTP 1.1-standarden får webbläsaren maximalt göra två samtidiga anslutningar till en och samma server, vilket innebär att alla försök till att hämta data från servern kommer att köas upp tills det finns en ledig anslutning. Detta inkluderar alla javascript, stylesheets, bilder, HTML-dokument etc. Därför är det dels bra att schemalägga första lyssnandet på servern enligt ovan till några sekunder efter att sidan har laddats för att alla bilder, stylesheets och javascript ska hinna hämtas korrekt. Se också till att låta eventuella uppköade anrop få någon sekund på sig att köras igenom innan du ställer en ny fråga till servern.
  • Ett bra tips är att flytta alla statiska resurser till en annan domän, t.ex.: resources.dindoman.se. Det frigör två extra HTTP-anslutningar.
  • Försök samordna alla dina events från servern till en och samma kanal – skicka gärna ett enkelt objekt till klienten som beskriver detaljer om alla typer av server-event och låt klienten avgöra hur de ska hatneras – voila en s.k. event-hubb!
  • Skriv inte till sessionen i din eventlyssnare om det går att undvika. Om du gör det kommer det krävas att du har [AjaxPro.AjaxMethod(AjaxPro.HttpSessionStateRequirement.ReadWrite)] vilket kommer låsa hela sessionen under tiden som du väntar på svar.
  • Tänk också på att göra så få anrop till servern som möjligt, baka gärna ihop dina bilder till en stor PNG som du sedan positionerar med hjälp av CSS, baka också gärna ihop så mycket som möjligt av dina CSS- och JavaScript-filer.
  • Använd FireBug och Yslow för att analysera vad som tar tid i din applikation.
  • Hantera att events kan hända under tiden som din klient inte lyssnar genom att skicka med en tidpunkt (t.ex UTC tiden på servern) för ditt senaste mottagna event.
  • Hantera timeout-fel i javascript genom att ställa nya requests till servern (lägg bara till en extra callback sist i ditt AJAX-anrop till servern).
  • Experimentera med hur lång tid servern ska vänta på händelser och hur lång tid klienten ska vänta innan ett nytt anrop ska ställas.
  • Experimentera med standardvärdet för timeout i AjaxPro – standard är åtta sekunder.
  • Använd inte SetInterval - om något oförutsett händer på servern eller klienten kommer SetInterval köa upp massor av nya förfrågningar som alla kommer starta så fort det finns möjlighet.Använd SetTimeout istället längst ner i din callback

Det absolut viktigaste av ovanstående är att inte använda AjaxPro.HttpSessionStateRequirement.ReadWrite. Om du gör det kommer att alla sidor i din applikation tar mellan noll och fem sekunder extra att exekvera och det är en väldigt svårsökt nöt att knäcka om man inte känner till den.

Lycka till!

Christian

Christian Landgren
2007-12-03