Sådan kodes server-side cache

Loadingtider mindskes, ressourceforbruget går ned og databaseserveren kan snuppe sig en lur, når man implementerer server-side cache til sit website. Med ganske få liniers kode øges effektiviteten med 400 procent i vores eksempel.

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
Cache er jo midlertidig lagring af indhold, og det er vigtigt at brugerne altid får den nyeste version af dette indhold. Med andre ord skal man programmere en mekanisme, som gør at cachen bliver opdateret, når indholdet ændres. Lad os se på et konkret eksempel, som kun berører cache-teknikker og ikke er optimeret på anden måde.

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 genererForsiden(), for at gemme den genererede HTML-kode i en applikationsvariabel:

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.




Brancheguiden
Brancheguide logo
Opdateres dagligt:
Den største og
mest komplette
oversigt
over danske
it-virksomheder
Hvad kan de? Hvor store er de? Hvor bor de?
Ciklum ApS
Offshore software- og systemudvikling.

Nøgletal og mere info om virksomheden
Skal din virksomhed med i Guiden? Klik her

Kommende events
Computerworld Cloud & AI Festival 2025

Med den eksplosive udvikling indenfor cloud & AI er behovet for at følge med og vidensdeling større end nogensinde før. Glæd dig til to dage, hvor du kan netværke med over 2.400 it-professionelle, møde mere end 50 it-leverandører og høre indlæg fra +90 talere. Vi sætter fokus på emner som AI; infrastruktur, compliance, sikkerhed og løsninger for både private og offentlige organisationer.

17. september 2025 | Læs mere


IT og OT i harmoni: Sikring uden at gå på kompromis med effektiviteten

IT og OT smelter sammen – men med risiko for dyre fejl. Få metoder til sikker integration med ERP, kundesystemer og produktion. Tilmeld dig og få styr på forskellene og faldgruberne.

24. september 2025 | Læs mere


NIS2: Vi gør status efter tre måneder og lærer af erfaringerne

Vær med, når vi deler oplevelser med implementering af NIS2 og drøfter, hvordan du undgår at gentage erfaringerne fra GDPR – og særligt undgår kostbar overimplementering.

30. september 2025 | Læs mere