Applikationsvariabler
I den forrige artikel om server-side cache så vi på de forskellige muligheder, der er for at cache websiderne, og dermed minimere ressourceforbruget og nedsætte loadingtiderne. I denne artikel kigger vi på et konkret eksempel.
Som vi tidligere så, kan de spørgsmål, man skal afklare ved implementering, deles i to: Hvad skal man vælge at cache, og hvordan skal data gemmes. Det første spørgsmål besvarede vi ved at dele op i tre typer af caching: Cache af data fra datakilder, cache af enkelte dele af den producerede HTML-kode og cache af hele sider.
Data-caching
Data-caching kan benyttes til de tilfælde, hvor selve datafangsten kræver en større processering. Et typisk eksempel er komplekse SQL-sætninger eller blot dataudtræk, der kræver et stort antal SQL-forespørgsler. Et praktisk eksempel herpå kan være visninger af træstrukturer, såsom en trådet visning i et debatforum.
Det kan være, at de samme data vises forskelligt på de enkelte sider, og dermed kan behovet opstå for at gemme data i en struktur. Dette kræver en eller anden form for serialisering, som er den proces, hvor et objekt omskabes til sekventielle data. Både PHP, ASP og JSP har metoder til at serialisere objekter.
Element-caching
Hvis data derimod præsenteres på samme måde på alle siderne, giver element-caching endnu bedre ydelse. Her genereres dele af HTML-koden og placeres i cache. Hvis elementet benyttes ofte, som for eksempel en forside, så kan det bedst betale sig at placere elementet i serverens hukommelsesrum ved en applikationsvariabel. Man skal selvfølgelig ikke fylde hukommelsen med variable, så det er kun de allermest brugte elementer, man skal gemme på denne facon. Andre elementer kan man så gemme i en tabel i databasen.
Den sidste type af cache - nemlig at generere statiske HTML-sider via et publiceringssystem - ser vi bort fra i denne artikel, da denne situation ikke dækker over et egentligt dynamisk website.
Et eksempel
Versioner, triggers og opdatering
Lad os forestille os, at vi har en nyhedsportal, hvor en række artikler ligger i en tabel i en database. Vi genererer forsiden ved at udtrække hver artikels overskrift (som kaldes "rubrik" i jargonen), og udtrække artiklens oprettelsestidspunkt. Det kunne se sådan ud i ASP-dialekten JScript, Microsofts JavaScript-implementering:
<%@ Language=JScript %>
<html>
<body>
<%
function genererForside()
conn = Server.createobject("ADODB.Connection");
conn.Open("website");
artikler = conn.execute(
"SELECT DATO, RUBRIK, ID " +
"FROM ARTIKLER " +
"ORDER BY DATO DESC"
);
while(!artikler.eof) {
paenDato = new Date(artikler(0)+"");
Response.write("<small class=\"dato\">"
+ paenDato.toLocaleString()
+ "</small>"
+ "\n"
+ "<h2 class=\"forside-rubrik\">"
+ "<a href=\"/vis_artikel.asp?ID="
+ artikler(2) + "\">"
+ artikler(1)
+ "</a>"
+ "</h2>"
+ "\n"
+ "<hr />"
+ "\n");
artikler.movenext();
}
artikler.Close();
conn.Close();
}
genererForside();
%>
</body>
</html>
Hvis siden har et stort antal eksponeringer, kan vi i stedet for at sende siden til browseren og gemme den genererede HTML-kode i en applikations-variabel.
Gemt HTML
Vi behøver kun ganske få omskrivninger af funktionen
function genererForsiden() {
conn = Server.createobject("ADODB.Connection");
conn.Open("website");
artikler = conn.execute(
"SELECT DATO, RUBRIK, ID " +
"FROM ARTIKLER " +
"ORDER BY DATO DESC"
);
var forside = "";
while(!artikler.eof) {
paenDato = new Date(artikler(0)+"");
forside += "<small class=\"dato\">"
+ paenDato.toLocaleString()
+ "</small>"
+ "\n"
+ "<h2 class=\"forside-rubrik\">"
+ "<a href=\"/vis_artikel.asp?ID="
+ artikler(2) + "\">"
+ artikler(1)
+ "</a>"
+ "</h2>"
+ "\n"
+ "<hr />"
+ "\n";
artikler.movenext();
}
Application("forsideKode") = forside;
artikler.Close();
conn.Close();
}
I stedet for at udskrive koden til browseren med Response.write(), opsamler vi koden i den lokale variabel forside, og gemmer til sidst koden i applikations-variablen forsideKode.
Opdatere hvornår
Når forsiden skal opdateres
Ideen er, at vi kun skal kalde funktionen genererKode, når det er absolut nødvendigt. Til at begynde med er variablen forsideKode tom, så vi skal i hvert fald starte med at gennemløbe funktionen. Dernæst skal vi opdatere forsideKode ved at kalde genererForside(), når forsiden skal opdateres, for eksempel når der bliver lagt en ny artikel ind i artikel-tabellen.
Det holder vi styr på ved en anden applikationsvariabel, som vi kan kalde opdaterForsiden. Når en ændring i systemet giver anledning til at opdatere, sættes variablen til sand (true). Efter at genererForside() er kaldt, sætter vi opdaterForsiden til falsk (false), da vi jo så netop har opdateret forsiden. Det kunne se sådan ud:
<%
if( (!Application("forsideKode"))
|| Application("opdaterForsiden") )
{
genererForsiden()
Application("opdaterForsiden") = false;
}
Response.write(Application("forsideKode"));
function genererForsiden() {
...
...
%>
I den øverste if-sætning, spørger vi om forsideKode er defineret, eller om variablen opdaterForsiden er sand. I begge tilfælde skal vi kalde genererForsiden(), og det gøres i den næste linie. I den tredie linie sætter vi opdaterForsiden til falsk, og den næste linie - uden for if-sætningen - udskriver vi indholdet af forsideKode.
Vi har prøvet at teste, hvor stor forskellen var i udførelsen i forhold til de to situationer, hvor HTML-koden genereres, og hvor koden læses fra applikationsvariablen. Det gjorde vi ved at tilføje linien
starttid = new Date()
i toppen og i bunden af scriptet linierne:
sluttid = new Date();
Response.write("<h1>"
+ (sluttid.valueOf() - starttid.valueOf())
+ "</h1>");
Kort fortalt gemmer vi tidspunktet, hvor scriptet starter og tidspunktet hvor scriptet slutter, og udskriver tidsforskellen i millisekunder (sluttid.valueOf() - starttid.valueOf()). I dette konkrete tilfælde lå tidsforbruget, hvor forsiden genereres på omkring 200 millisekunder, mens situationen, hvor koden læses fra applikations-variablen, lå på omkring 50 millisekunder. Altså en reduktion til 25 procent, hvilket vel må siges at være ganske pænt i betragtning af, hvor få liniers kode der er til forskel i de to scripts.
Som det forhåbentlig ses, er det ikke så svært at implementere server-side cache. Eksemplet ovenfor er selvfølgelig en skitse, men princippet er ganske simpelt: Gem den genererede HTML-kode i en applikations-variabel, eller i en tabel i databasen, og hold styr på hvornår cachen skal genopfriskes.