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.

Annonce

BEC Financial Technologies

Development Manager for Core Banking

Region Sjælland

Netcompany A/S

IT Consultant

Nordjylland

Politiets Efterretningstjeneste

Vil du bygge API'er uden en facitliste i IT-afdelingen i PET?

Københavnsområdet

Event: Computerworld Summit 2027 - Aarhus

Digital transformation | Aarhus C

Styrk din digitale strategi med konkret brug af AI og ny teknologi. Mød 250 it-professionelle, få indsigter, løsninger og netværk på én dag. Computerworld Summit i København viser hvordan teknologi skaber forretningsværdi – her og nu.

25. maj 2027 | Gratis deltagelse

Navnenyt fra it-Danmark

Systematic A/S har pr. 1. august 2026 ansat Casper Voldstedlund, 52 år,  som Director, Products. Han skal især beskæftige sig med drive produktudvikling inden for national sikkerhed og kritisk infrastruktur (NSI). Han kommer fra en stilling som Senior Director hos Stibo Systems A/S. Nyt job

Casper Voldstedlund

Systematic A/S

Softwarevirksomheden Idura, som udvikler løsninger til digital identifikation og autentifikation, har pr. 1. september 2026 ansat Maria Juliane Friedrichsen, 47 år,  som Office Manager. Hun skal især beskæftige sig med at holde overblik og sikre, at alt på kontoret kører, som det skal. Hun kommer fra en stilling som Office Manager hos Kontorhotellet H2M. Hun er uddannet professionsbachelor i ernæring og sundhed og har en korrekturlæseruddannelse. Hun har tidligere beskæftiget sig med korrekturlæsning og den daglige drift af kontorer. Nyt job

Maria Juliane Friedrichsen

Softwarevirksomheden Idura, som udvikler løsninger til digital identifikation og autentifikation,

Lars Erik Larsen, it-arkitekt hos Jyske Bank, har pr. 29. juni 2026 fuldført uddannelsen Master i it, linjen i softwarekonstruktion på Aarhus Universitet via It-vest – samarbejdet. Færdiggjort uddannelse

Lars Erik Larsen

Jyske Bank

Norriq Danmark A/S har pr. 3. august 2026 ansat Jeppe Lindberg som Developer. Han skal især beskæftige sig med at være med til at analysere, designe og udvikle skræddersyede forretningsløsninger til vores kunder i Business Central. Han kommer fra en stilling som Business Central Developer hos Evexo. Han er uddannet hos Aalborg Universitet- Master's degree, Computer Software Engineering. Nyt job

Jeppe Lindberg

Norriq Danmark A/S