Mall för examensarbete

ls
k
Mal
ri
k
vHen
pada
a
REALTIDSBASERAD
KUNDVAGNSTILLÄMPNING SOM
NÄTVERKSAPPLIKATION INOM
E-HANDEL MED NODE.JS
En jämförelse av kundvagnstillämpningars
påverkan på server
REAL-TIME SHOPPING CART AS
NETWORK APPLICATION WITH
NODE.JS
A comparison of shopping cart applications
impact on server
Examensarbete inom huvudområdet Datalogi
Grundnivå 30 högskolepoäng
Vårtermin 2015
Linus Frykgård Jäglid
Handledare: Marcus Brohede
Examinator: Mikael Berndtsson
Sammanfattning
E-handelns utsträckning har ökat markant de senaste åren och som en följd ställs det
högre krav på att webbplatser ska kunna hantera det ökande antalet användare. En
viktig funktionalitet en e-handelssida ska ha är att webbplatsen har någon form av
kundvagnslösning för att hantera varor inför ett eventuellt köp. En av de vanligaste
lösningarna för webbapplikationer är att PHP exekveras på Apache. Att användare
upplever långsamma laddningstider med en webbapplikation bidrar till att
användaren istället väljer att besöka en annan webbplats. Arbetet undersöker om
Node.js kan ersätta Apache som webbserver. Mätningar utförs för att ta reda på
skillnaderna på svarstid och serverbelastning mellan Node.js och Apache för att ge
understöd för om ett eventuellt beslut om ett byte av webbserver är lönsamt. Resultatet
visar att Node.js har en lägre svarstid än Apache. Däremot har Node.js en högre
serverbelastning.
Nyckelord: PHP, Apache, Node.js, Webbserver, Serverbelastning, Svarstid
Innehållsförteckning
1
2
Introduktion ........................................................................................................ 1
Bakgrund ............................................................................................................ 2
2.1
2.2
2.3
2.4
2.5
Kundvagn inom e-handel ........................................................................................ 2
PHP ........................................................................................................................ 2
Apache ................................................................................................................... 3
Node.js ................................................................................................................... 4
Databaser ............................................................................................................... 5
2.5.1
2.5.2
3
Problemformulering .......................................................................................... 7
3.1
3.2
3.3
3.4
4
Hypotes .................................................................................................................. 7
Metodbeskrivning .................................................................................................... 8
Alternativa metoder ................................................................................................. 8
Etiska aspekter ....................................................................................................... 9
Genomförande ................................................................................................. 10
4.1
4.2
4.3
Val av applikation .................................................................................................. 11
Litteraturstudie ...................................................................................................... 12
Pilotstudie ............................................................................................................. 12
4.3.1
4.3.2
4.4
5
Antal varor ..................................................................................................................... 14
Serverbelastning ........................................................................................................... 15
Progression........................................................................................................... 16
Utvärdering....................................................................................................... 17
5.1
Presentation av undersökning ............................................................................... 17
5.1.1
5.1.2
5.1.3
5.2
6
Databashanterare............................................................................................................ 5
Cache .............................................................................................................................. 6
Svarstid på server ......................................................................................................... 18
Serverbelastning ........................................................................................................... 20
Loggning på server och kodfil ....................................................................................... 21
Analys och slutsatser ............................................................................................ 22
Avslutande diskussion .................................................................................... 23
6.1
6.2
6.3
Sammanfattning .................................................................................................... 23
Diskussion ............................................................................................................ 23
Framtida arbete .................................................................................................... 24
Referenser .............................................................................................................. 26
1 Introduktion
E-handelns utsträckning har ökat markant de senaste åren och som en följd ställs det högre
krav på att webbplatser ska kunna hantera det ökande antalet användare. Enligt Davarzani
och Norrman (2015) kommer e-handeln att fortsätta öka i omfattning och ta över en allt
större andel ifrån traditionella fysiska butiker. De menar även att detta ställer högre press på
att stora butikskedjor delvis övergår till att även erbjuda kunder att handla via internet.
En viktig funktionalitet en e-handelssida ska ha är att webbplatsen har någon form av
kundvagnslösning som erbjuder kunderna att lägga till, ta bort och betala för sina varor.
Förutom funktionaliteten för en sådan lösning är det av stor vikt att inga överbelastningar på
webbservern sker på grund av många samtidiga användare. Sett ur ett användarperspektiv
kan överbelastningar på webbservern bland annat få webbplatsen att agera långsamt. Detta
kan få kunderna att lämna webbplatsen och besöka en e-handelssida hos ett annat företag
istället. För e-handelssidor kan detta i sin tur leda till att de går miste om eventuella
försäljningar och alltså gå med lägre ekonomisk vinst.
Det finns en rad olika webbservrar där den mest populära är Apache (Portokalidis och
Keromytis 2010). Apache tillsammans med PHP är det vanligaste sättet att utveckla
webbapplikationer. Exempelvis används det bland annat till att utveckla och driva
kundvagnsapplikationer på webbplatser. Ett relativt nytt programmeringsspråk är Node.js,
som används för att utveckla och driva webbapplikationer. Det är utvecklat i JavaScript och
körs på Googles JavaScript-motor (V8). Node.js är så kallat händelsebaserat där det för varje
nytt anrop skapas ett event där fördelen är att anropen inte riskerar att läggas på kö som
med fler-trådiga processer. Detta gör att Node.js normalt sätt inte drabbas av samma
problem Apache har med att webbservern överbelastas när ett större antal användare
besöker webbplatsen.
Syftet med detta examensarbete är att undersöka skillnaderna på svarstid och
serverbelastning mellan Node.js och Apache som webbserver. Mätningarna utförs med två
kundvagnsapplikationer utvecklade i JavaScript och PHP. Applikationen utvecklad i
JavaScript exekveras på Node.js och den PHP-utvecklade exekveras på Apache.
Utifrån en rad kriterier på bland annat öppen källkod och minimal funktionalitet väljs en
PHP-baserad kundvagnsapplikation som kommer vara baslinje i mätningarna. Därefter
utvecklas en kundvagnsapplikation i Node.js som är direktöversatt ifrån PHP-applikationen
där skillnaderna endast är syntaxer för respektive programmeringsspråk samt
programmeringsmodell. Kundvagnsapplikationerna används sedan till att utföra mätningar
på svarstid och serverbelastning på Node.js och Apache.
En pilotstudie genomförs med två olika typer av mätningar för respektive webbserver med
syftet att få en grundläggande bild av hur serverbelastningen för webbservrarna skiljer sig åt
samt att ta reda på om antalet varor är en parameter som kan påverka svarstiden. Därefter
utformas ett experiment utifrån resultat som framkommit i pilotstudien med syfte att mäta
serverbelastning och svarstid. Mätningarna genomförs med en webbläsare på klientsidan där
ett skript med ett förbestämt klickmönster används för att simulera klick. Svarstiden för
varje klick mäts både på klient- och serversidan medan serverbelastningen endast mäts på
serversidan.
1
2 Bakgrund
I samband med att allt fler väljer att köpa produkter via internetbutiker ställs högre krav på
att e-handelssidornas webbservrar ska kunna hantera ett högt antal samtidiga användare.
Det traditionella sättet webbservrar är baserade på är med så kallade fler-trådiga processer.
Detta innebär att det i en situation med ett flertal samtidiga anrop skapas trådar som läggs
på rad i väntan på att programkoden i föregående tråd ska exekveras. Som en följd av detta
kan webbservern bli överbelastad och användaren upplever att webbplatsen agerar
långsamt.
De flesta kundvagnsapplikationer idag är utvecklade med PHP och använder Apache som
webbserver. PHP-anrop ifrån klientsidan skickas vidare till webbservern där anropen
bearbetas och sedan skickar tillbaks ett svar. I takt med att e-handelns omsättning ökar så
blir även påfrestningarna på webbservrarna högre. En webbserver har en begränsad
processorkapacitet som med en PHP/Apache-lösning enkelt förbrukas med ett tusental
samtidiga användare. Vid överbelastning av webbservern tycks webbplatsen agera långsamt.
2.1 Kundvagn inom e-handel
Traditionellt sett är en kundvagn ett fysiskt föremål varpå kunden använder för att tillfälligt
lagra sina varor inuti under sin shoppingrunda i en butik. Enligt Close och Kukar-Kinney
(2010) används en elektronisk kundvagn på liknande sätt till att låta kunden lagra varor
inför ett eventuellt påföljande köp på en e-handelssida. Grundläggande funktionalitet som
en elektronisk kundvagn bör ha är att det är möjligt för kunden att lägga till, uppdatera och
ta bort varor. Det ska givetvis även vara möjligt att bekräfta och genomföra sitt köp.
2.2 PHP
Hills, Klint & Vinju (2013) beskriver PHP som ett serverbaserat skript-språk som går under
öppen källkod med syftet att driva webbplatser med dynamiskt innehåll. Ursprungligen
skapades PHP som en parser för att förenkla programmering av webbsidor genom att
ersätta HTML-taggar med kod. Efter att intresset ökat har det släppts en rad förbättringar
till skript-språket. Till de främsta områden PHP används inom hör internetforum, formulär
för inloggning, bildgalleri, webbapplikationer med mera.
Några fördelar med PHP som utvecklare upplever menar Machlis (2002) är att det är relativt
enkelt att lära sig tack vare sina inbyggda funktioner som exempelvis gör det enkelt att skapa
kopplingar till databaser. Machlis (2002) nämner även att det är enkelt att migrera PHP-kod
mellan olika servrar på grund av sin universella standard.
Webbplatser med statiskt innehåll genomgår inga förändringar när det kompileras och visas
för användaren. D.v.s. att den kod som från början är skriven för en webbplats ser likadan ut
då den når en besökare på webbplatsen. Nackdelen med detta är att det blir problematiskt i
de fall det är önskvärt att dynamiskt generera kod som visas beroende av hur användaren
interagerar med webbplatsen eller andra faktorer. Ett inloggningsmeddelande med
användarens namn som hämtats ifrån databasen är ett exempel på hur dynamiskt genererad
kod kan användas.
2
2.3 Apache
Enligt Cao, Andersson, Nyberg och Kihl (2003) hör Apache till en av de mest använda
webbservrarna. Likt webbservrar generellt har Apache till uppgift att hämta lagrade kodfiler
en webbläsare efterfrågar, kompilera koden och sedan presentera resultatet. Apache och
PHP är en vanlig kombination för att skapa och driva webbapplikationer på internet.
Apache består av en rad olika konfigurationer med skillnader för hur anrop till webbservern
behandlas. Mer ingående behandlas anrop genom trådar och/eller processer som skapas och
för varje konfiguration finns olika för- och nackdelar. Temme, S. (2007) menar att det för
varje operativsystem finns speciella konfigurationer, så kallade MPM:er (Multi Processing
Modules). För Unix-baserade plattformar är de mest använda konfigurationerna MPM
Prefork och MPM Worker. De övergripande skillnaderna mellan Unix-konfigurationerna är
att MPM Prefork använder processer och MPM Worker är en slags fler-trådig hybrid som
använder både processer och trådar.
Behrends, Stirewalt & Dillon (2005) hävdar att upphovsmännen bakom skriptmotorn PHP
varnat för att flertrådiga konfigurationer av Apache inte bör användas tillsammans med
PHP. Detta eftersom PHP är beroende av en rad olika tredjeparts bibliotek som inte kan
garantera trådsäkerhet. Däremot är konfigurationen MPM Prefork trådsäkert och tillåter
icke-trådsäkra tredjepartsbibliotek (Apache, 2015, 18 mars).
MPM Prefork använder singeltrådiga processer som hanterar ett anrop i taget. Detta innebär
att när webbservern startas skapas en huvudprocess som i sin tur innehåller underprocesser.
Varje underprocess ansvarar för ett anrop i taget och får tilldelat en del av minnet på
webbservern. Hur stor minneskapaciteten varje underprocess får bestäms av en person med
administratörrättigheter till webbservern samt beroende av hur stort utrymme som finns
tillgängligt totalt på minnet. När taket av det maximalt begränsade antalet processer är nådd
blir resultatet att Apache nekar åtkomst för ytterligare anrop.
Figur 1
Exempel på en process-baserad anslutning med Apache
3
2.4 Node.js
Node.js är en plattform som är byggd på Chromes JavaScript-motor (V8) med syftet att
skapa webbapplikationer och webbservrar. Vanligtvis exekveras JavaScript-kod i en
webbläsare på klientsidan men med Node.js förflyttas istället ansvaret till serversidan. Fokus
ligger på att erbjuda snabba nätverksapplikationer med låg minneskonsumtion på
webbservern.
Node.js är händelsebaserat vilket betyder att det använder händelsebaserade interaktioner.
En anslutning till webbservern upprättas med händelser (events) och har en konstant
anslutning mellan klient och webbserver på endast en aktiv tråd (Tilkov & Vinoski, 2010).
Händelsebaserade events som upprättas bidrar till att Node.js inte utsätts på samma sätt för
risken att många samtidiga processer konsumerar hela den tilldelade minneskapaciteten.
Till skillnad från webbservrar baserade på fler-trådiga processer är Node.js baserad på
asynkrona händelser. Att Node.js är asynkront innebär att kod kan exekveras parallellt utan
att behöva vänta på att föregående genomförts. I en nätverksapplikation skulle till exempel
synkroniserade klick på två knappar exekvera tillhörande kod samtidigt.
Figur 2
Exempel på en händelsebaserad anslutning med Node.js
Den asynkrona hanteringen medför även att deadlock inte kan uppstå. Ett så kallat deadlock
uppstår när två trådar fastnat i en loop och väntar på att den andra ska exekveras. Ingen av
trådarna kommer att exekveras och nya trådar som skapas kommer bara läggas på kö utan
att kunna exekveras. Enligt Griffin, Elger, och de Leastar (2013) är deadlock en bidragande
orsak till att webbplatser överbelastas och tillfälligt ligger nere.
Den stora fördelen med Node.js blir alltså att ett stort antal samtidiga anslutningar inte
märkvärt påverkar webbservern. Gribble, Brewer, och Culler (2000) menar att flaskhalsen är
processorn på webbservern och trots att antalet anslutningar ökar är genomströmningen den
samma men att fördröjningen av anslutningarna ökar.
4
2.5 Databaser
En databas är en samling av data av olika slag som finns lagrad på en server. Data kan
exempelvis bestå av uppgifter för en registrerad användare på en webbplats med
användarens namn och email-adress. För att fortsätta på exemplet om användaruppgifter
kan det sägas att data om en användare bör vara tillräckligt strukturerad för att kunna
urskilja vilka uppgifter som hör till vilken användare. Detta är vad en relationsdatabas
används till genom att data lagras i så kallade relationer som är det samma som en tabell. I
tabellerna lagras data på olika rader med namngivna kolumner som talar om vilken typ av
data varje kolumn innehåller. Raderna brukar kallas tupler medan kolumnerna kallas
attribut. Tabell 1 visar ett exempel på hur data i en relationsdatabas kan se ut.
Tabell 1 Relationsdatabas exempel
ID
Användarnamn
Email-adress
1
Sven
[email protected]
2
Per
[email protected]
3
Johan
[email protected]
4
Fredrik
[email protected]
2.5.1 Databashanterare
Databashanterare har till uppgift att lagra och organisera databaser av olika slag i så kallade
datafiler. En av de populäraste databashanterarna för relationsdatabaser är MySQL som går
under öppen källkod och finns tillgängligt för alla de vanligaste operativsystemen (Bell,
2012). De vanligaste tillämpningsområdena för MySQL är att koppla samman en webbplats
som behöver lagra data med en databas. Alla stora programmeringsspråk har stöd för att
använda MySQL där det för varje språk är möjligt att skapa en koppling med vanligtvis
inbyggd funktionalitet. Exempelvis används funktionen PDO i PHP till att skapa en koppling
till databasen där servernamn, användarnamn, lösenord och databasnamn anges. Därefter är
det möjligt att ställa frågor till databasen och på så sätt hämta, modifiera eller ta bort data.
Hämtad data kan sedan presenteras på webbplatsen. Exemplet nedan visar hur SQL-frågor
kan se ut där användaruppgifter läggs till i tabellen user där sedan all data om användarna
hämtas och sorteras i fallande ordning utifrån ID.
INSERT INTO ’user’ (’id’, ’username’, ’email_address’) VALUES
(1, ’Sven’, ’[email protected]’),
(2, ’Per’, ’[email protected]’),
(3, ’Johan’, ’[email protected]’),
(4, ’Fredrik’, ’[email protected]’);
SELECT * FROM user ORDER BY id ASC;
5
2.5.2 Cache
Att ställa en fråga till databasen där resultatet är detsamma som vid det senaste tillfället
samma fråga ställdes kan anses vara onödig upprepning. Cache är ett begrepp som innebär
att data som frekvent används lagras för att återanvändas. Syftet är att data snabbt ska
kunna hämtas och på så sätt minska exekveringstiden. Khanuja & Adane (2012) menar att en
av fördelarna med MySQL är att resultatet ifrån varje SELECT-fråga som ställs cachas. Ställs
en SQL-fråga som vid ett tidigare tillfälle redan ställts returneras det cachade resultatet
istället för att på nytt ställa frågan och hämtar resultatet.
Figur 3
Exempel på hur cachad data hämtas
Vid varje SQL-fråga som ställs genomför MySQL en enkel kontroll om data i en tabell
uppdaterats. Har tabellen uppdaterats så ställs frågan på nytt för att hämta uppdaterad data,
annars hämtas resultat som cachats. Figur 3 beskriver hur en fråga ställs på klientsidan,
genom databashanteraren och vidare till databasen där ett cachats resultat hämtas istället
för att hämta data på nytt. Resultatet returneras sedan tillbaks till klientsidan.
6
3 Problemformulering
Att användare upplever långa laddningstider för att ladda en webbsida leder till otålighet och
är den vanligaste orsaken till att användaren avslutar besöket enligt Kaur & Dhindsa (2014).
På en e-handelssida är varje användare en möjlig kund och uppstår fördröjningar längre än
några sekunder riskerar kunden att istället besöka en konkurrerande aktörs e-handelssida.
Galletta, Henry, McCoy & Polak (2004) hävdar att en gyllene regel inom webbplatsdesign är
att laddningstiderna bör ligga på under 2 sekunder för att vara acceptabla hos användaren.
Långa laddningstider kan i det långa loppet leda till att företagets ekonomi påverkas negativt
då eventuella vinster företaget hade genererat ifrån kunderna inte längre uppstår p.g.a. det
minskande antalet användare på webbplatsen. Kaur & Dhindsa (2014) menar att en av de
bidragande orsakerna till problemet med laddningstiderna är överbelastade webbservrar.
Enligt Franke och Von Hippel (2003) är Apache den populäraste webbservern som bland
annat används inom e-handeln för att driva kundvagnsapplikationer. Apache i kombination
med PHP används för att hämta och skicka data till databasen för att exempelvis visa upp
det nya totalpriset efter att ännu en vara lagts till i kundvagnen. Funktionsmässigt fungerar
denna lösning bra och under alla år PHP och Apache har funnits har säkerhetsaspekten hela
tiden förbättrats.
Problemet med Apache som webbserver är den process/tråd-baserade hanteringen av anrop.
Begränsningen på antalet tillåtna processer och trådar för varje anrop som webbservern kan
hantera bidrar till att minneskapaciteten snabbt kan konsumeras. Uppnås det maximala
antalet aktiva trådar kan inga fler anrop exekveras och läggs istället på kö i väntan på
föregående anrop. Ett större antal användare som samtidigt lägger till, uppdaterar eller tar
bort varor ifrån kundvagnen kan då snabbt förbruka den maximala minneskapaciteten och
blir ett problem för webbservern. Bortsett ifrån de problem som uppstår på webbservern blir
det ur ett användarperspektiv längre laddningstider som kan leda till att användaren
avslutar sitt besök på webbplatsen.
Ojamaa & Duuna (2012) beskriver Node.js som en JavaScript-baserad plattform som
installeras på webbservern och fungerar händelsebaserat. Händelsebaserade webbservrar
bygger på att en enskild tråd skapas vid en förfrågan där samma tråd hålls aktiv genom hela
sessionen och hanterar samtliga nyskapade anrop. Fördelarna jämfört med flertråd-baserade
webbservrar som Apache blir att endast en tråd skapas för varje användare. Genom denna
kan åtskilliga anrop skickas mellan webbserver och klient utan att trådar behöver läggas på
kö. På så sätt förbrukas inte lika mycket av minneskapaciteten på webbservern.
En migration från Apache till Node.js som webbserver bör möjliggöra hantering av ett större
antal samtidiga användare utan att webbservern överbelastas. Det är av intresse att
genomföra en undersökning av skillnader för serverbelastning och laddningstid de två olika
webbservrarna har som driver en kundvagnsapplikation tillsammans med en databas.
3.1 Hypotes
Hypotesen är att en kundvagnsapplikation skriven i JavaScript och exekverad
på Node.js kommer ha lägre serverbelastning och svarstider jämfört med
samma applikation skriven i PHP och exekverad på Apache.
7
3.2 Metodbeskrivning
För alla typer av projekt är vattenfallsmetodiken ett vanligt förekommande arbetssätt menar
Stober och Hansmann (2010). Tankesättet bakom denna arbetsform är att projektet delas
upp olika steg där varje steg måste vara avklarat innan nästa kan påbörjas. Uppstår det
problem i ett av stegen är det möjligt att gå tillbaka till det föregående och rätta till problem
som uppstått där. Detta arbetssätt lämpar sig bra i detta projekt eftersom ingen kund är
förknippad med projektet.
Enligt Basili (1996) grundar sig vetenskapligt framtagen programvara i att experiment
använts under utvecklingsprocessen. Att i en laboratoriemiljö analysera programvaran
bidrar till ett mer pålitligt slutresultat. Olika steg som hör till menar han är att det först ska
finnas en tydlig teori i arbetet. Detta följs av att programvaran ska testas och slutligen, om
möjligt, kunna styrka resultatet med empiriska observationer.
Genomförandet av experimentet kräver att en kundvagnsapplikation utvecklad i PHP
direktöversätts till Node.js för att få två funktionsmässigt identiska applikationer för
respektive webbserver och som inte påverkar mätresultaten. Kundvagnsapplikationen
utvecklad i PHP kommer tillsammans med Apache som webbserver användas som baslinje
för mätningarna. Den andra kundvagnsapplikationen utvecklad i Node.js kommer mätas
emot baslinjen och använder Node.js som webbserver. Data hämtas ifrån en gemensam
databas.
Experimenten kommer utföras med en testdator för själva utförandet av mätningarna och
ytterligare en testdator som agerar webbserver. Verktyg som exekverar JavaScript-kod i en
webbläsare kommer att användas för att simulera verkliga användares interaktion med
kundvagnapplikationerna. Användarna kommer att lägga till och ta bort butiksvaror i en
kundvagn. Detta kommer ske utifrån ett förbestämt mönster för att få identiska testfall för
båda applikationerna. Mätverktyg för att mäta belastning på server kommer också att
användas.
Mätresultat kommer att illusteras i grafer och kommer vara baserade på hur
minneskonsumtionen på webbservern förändras vid ett ökande antal samtidiga användare
som utför olika operationer. Vidare kommer mätresultat att illustreras i grafer över svarstid
vid olika antal användare. Detta kommer ge en tydlig bild över hur stor inverkan valet av
webbserver har på svarstid och serverbelastning.
3.3 Alternativa metoder
En alternativ metod till experiment skulle kunna vara att utföra en fallstudie. I grunden är en
fallstudie lik experiment med skillnaden att undersökningen sker i en verklig miljö istället
för i en laboratoriemiljö. Den stora fördelen blir att tester utförs på parametrar som endast
uppstår i en verklig miljö. Till detta arbete hade en sådan metod exempelvis kunnat utföras i
samarbete med ett företag som använder en kundvagnslösning och Apache som webbserver,
exempelvis en e-handelssida. Anledningen till att denna metod inte används är att
kundvagnslösningar företag använder består av fler komponenter än vad som kommer
undersökas i detta arbete.
8
3.4 Etiska aspekter
Metoden innefattar inga testpersoner och kommer därför inte behandla några former av
personuppgifter eller annan data som på något sätt kan kopplas till en fysisk person.
Beskrivande text för en produkt i respektive applikation är endast till för att skapa en mer
verklighetstrogen representation av en kundvagnsapplikation och bör inte anses stötande för
en individuell person eller folkgrupp. Ingångspunkten i detta arbete är att undersöka och
försöka hitta bevis för att Node.js eventuellt är bättre än PHP. Utöver det kommer inga
personliga ställningstaganden att tas för att favorisera den ena tekniken framför den andra.
Det finns heller inga personliga kopplingar till något företag och/eller organisation som kan
påverka metodutförande eller resultat.
För att möjliggöra en återupprepning av experiment kommer alla verktyg som används vara
öppen källkod och all programkod som skapas att finnas tillgänglig. Samtliga
konfigurationer för programvara och hårdvara kommer att anges. Testdata kommer inte att
publiceras offentligt utan endast redovisas i sammanställd form med hjälp av grafer.
9
4 Genomförande
I denna del beskrivs hur applikationen framställts, vilka inspirationskällor som ligger till
grund för detta arbete, vad pilotstudien visar och problem som uppstått under arbetets gång.
Det första steget i arbetet var att skapa en webbserver i Node.js. En av fördelarna med
Node.js är att det innefattar en inbyggd http-server som gör att det enkelt går att skapa en
webbserver. Mardan (2014) beskriver Express som ett ramverk som bygger på Node.js httpmodul. En av fördelarna Express erbjuder är sessionshantering samt hantering av
formulärdata (GET och POST) som funktionsmässigt liknar motsvarande funktionaliteter i
PHP. Av de anledningarna används detta ramverk till att skapa webbservern. Figur 4 visar
hur en webbserver skapas som lyssnar på port 8080. Utifrån dokumentation på
expressjs.com skapades webbservern och illustreras nedan med kod där webbservern lyssnar
på port 8080.
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(8080);
Figur 4
Skapa en webbserver med Express
Nästa steg var att skapa en koppling till databasen för att kunna ställa frågor för att hämta
data om produkterna. Howard (2012) beskriver att MySQL finns som ett ramverk för
Node.js som installeras externt och innehåller liknande funktionalitet databashanteraren
erbjuder till PHP. Ramverket används till att etablera en koppling där alla nödvändiga
uppgifter anges för att komma åt databasen. Därefter används objektet client i samband med
att ställa SQL-frågor. Tabeller med text och bilder finns lagrade i en databas som båda
applikationerna har tillgång till. Figur 5 visar hur en koppling till databasen skapas med
Express.
var mysql = require("mysql");
var client = mysql.createConnection({
"host": "localhost",
"port": 3306,
"user": "root",
"password": "manthesven",
"database" : "demo"
});
Figur 5
Skapa en databas med Express
Arbetet fortsatte sedan med att applikationen skapades med samma funktionalitet som PHPapplikationen där syntax och programmeringsmodell som är specifik för respektive
programmeringsspråk är enda skillnaden.
10
4.1 Val av applikation
Valet av PHP-baserad applikation (Sanwebe.com, 2015, 16 februari) bestämdes utifrån en
del kriterier som skulle uppfyllas. Kriterierna handlade om det är av stor vikt att
funktionalitet för att lägga till, ta bort och uppdatera varor finns med. Det var även viktigt att
applikationen var helt öppen källkod för att möjliggöra en replikering och att den inte var
utvecklad som ett plugin för Wordpress eller ett Content Management System (CMS). En
applikation utvecklad för ett CMS hade medfört att extra parametrar hade tillkommit och
varit tvungna att tas med i beräkningarna för mätningarna. Med söktermerna ”php shopping
cart” valdes 10 populära kundvagnsapplikationer och utvärderades baserat på kriterierna där
applikationen som uppfyllde flest kriterier blev den som slutligen valdes till projektet, se
tabell 2.
Tabell 2 Val av kundvagnsapplikation
Namn
Öppen källkod
Inte kopplat till CMS
Minimal
funktionalitet
Sanwebe.com
X
X
X
Codeofaninja.com
X
X
-
X-Cart
-
-
-
OpenCart
X
-
-
LiteCart
X
X
-
Cs.Cart
-
X
-
Shop-Script
X
X
-
OSCommerce
X
-
-
Zen Cart
X
-
-
Digistore
X
X
-
11
4.2 Litteraturstudie
Den största inspirationskällan till arbetet var den stora uppmärksamheten Node.js fått i
programmeringsvärlden runt om i olika forum och på bloggar. T. Capan är en av de som
skrivit ett inlägg om detta och han hävdar bland annat att Node.js har stor potential och i
många fall kan ersätta nuvarande webblösningar. Även Chaniotis, Kyriakou och Tselikas
(2014) framhäver fördelarna med Node.js som webbserver jämfört med Nginx och Apache.
Slutsatser de kunde dra utifrån sitt arbete var bland annat att Node.js är ett perfekt verktyg
vid utvecklande av snabba, skalbara webbapplikationer.
4.3 Pilotstudie
För att påvisa mätbarheten genomfördes en pilotstudie som innefattar två delar. Den första
delen i pilotstudien handlar om att ta reda på om det är någon skillnad på svarstiden för vid
olika antal varor som läggs till i kundvagnen. Detta är av stor vikt eftersom det kan komma
att spela in på hur experimentet sedan utformas. Den andra delen handlar om att ta reda på
hur hög serverbelastning de olika webbservrarna har. I en mätning som loadimpact.com
(2013) har gjort, framgår det att Node.js generellt har högre serverbelastning än exempelvis
Apache. Det är därför av intresse att mäta serverbelastningen i pilotstudien, likväl som i det
faktiska experimentet. Till mätningarna av den procentmässiga belastningen används ett
skript som med Linux-kommandot top (Stackoverflow.com, 2012) (appendix B). Topkommandot loggar belastningen på webbservern var tredje sekund (thegeekstuff.com, 2010).
Både i pilotstudien och senare till experimentet, används ett JavaScript som simulerar
knapptryck på applikationen (Appendix A). Det fungerar genom att ett simulerat klick lägger
till en eller flera varor och därefter tömmer kundvagnen på varor. Tiden som loggas är den
totala tiden det tar från ett genomfört knapptryck tills att servern skickat tillbaks ett svar och
visat upp förändrad data. Skriptets mönster för att lägga till och ta bort varor bestäms med
hjälp av alert-rutor där antal iterationer samt antal varor anges.
Även i applikationens kodfil loggas den tid det tar från att ett anrop ankommit tills att
kodfilen exekverat sin kod och ett svar skickats tillbaks. På detta sätt motsvarar skriptet på
klientsidan den totala tiden det tar att göra ett anrop och de inbäddade
loggningsfunktionerna i kodfilen för tiden det tar för ett anrop att ankomma och skickas
tillbaks som ett svar till klienten.
Figur 6
Terminologi för loggning
12
Figur 6 visar hur loggning på klientsidan och i kodfilen övergripande går till. Tiden på
klientsidan loggas mellan punkterna A  A’  C  D  B’  B vilket alltså är hela sträckan
från att klienten klickar på knappen, servern behandlar anropet och sedan skickar ett svar
tillbaks till klienten. Tiden det tar att exekvera kodfilen loggas i själva kodfilen och illustreras
med sträckorna C  D. Nätverkstiden ligger mellan sträckorna A  A’ + B’  B, men
eftersom mätningarna sker över ett lokalt nätverk kan det sägas att nätverkstiden är
försumbar och alltså är en parameter som inte påverkar mätningarna.
Figur 7
Terminologi för loggning utan nätverkstid
Figur 7 visar en hur mätningarna går till då nätverkstiden är försumbar. Svarstiden på
servern ligger mellan punkterna A’  C + D  B’ i figur 6. Eftersom denna specifika sträcka
inte loggas så finns det en lösning för detta som är att subtrahera kodfilens tid ifrån
klientsidans tid (klient - kodfil = server). Mer konkret kan detta utföras genom att först ta ut
ett klientdelta (Δ) och subtrahera sträckorna B’-A’ = Δ. Sträckorna C  D kallas för
kodfildelta (Δ2). För att sedan få ut svarstiden för servern subtraheras klientdelta med
kodfildelta, Δ – Δ2 = svarstid för server.
13
4.3.1 Antal varor
För att bestämma hur många varor som ska läggas till i det kommande experimentet och
säkerställa att resultat cachas, utfördes ett test för att mäta om svarstiden påverkas när olika
antal varor läggs till. Även detta test genomfördes med en användare i 3 mätserier med 100
interaktioner för varje antal varor. Antalet varor för varje test ändrades mellan 1, 2, 4, 8 och
16 varor. Varje användare simuleras med en ny process av webbläsaren Firefox, anledningen
till att en ny process skapas är för att göra det så verklighetsbaserat som möjligt. Det är också
viktigt att varje användare får en egen LocalStorage allokerad till sig för att inte blanda ihop
variabler som styr iterationerna i skriptet.
Millisekunder
Antal varor
10
9
8
7
6
5
4
3
2
1
0
Apache
Node.js
1 vara
2 varor
4 varor
Figur 8
8 varor
16 varor
Olika antal varor
Figur 8 visar resultatet för olika antal varor på Apache. Mätningarna visar ganska jämna
svarstider i graferna, både för Node.js som Apache. Intervallen ligger generellt mellan 2,5-3
millisekunder för Apache och 4-4,5 millisekunder för Node.js. I Appendix C och D finns
motsvarande linjegrafer för figur 8 som visar att första SQL-frågan som ställs har en högre
svarstid än de övriga SQL-anropen. Anledningen till att svarstiden i början är hög beror på
att det är då SQL-frågan som ställs till databasen cachas. Detta eftersom att MySQL cachar
varje SQL-fråga för att undvika redundans. Slutsatser som kan dras är att oavsett hur många
varor som läggs till så verkar svarstiden vara densamma. Antalet varor kommer inte vara en
parameter som har betydelse och därför kommer endast en vara att läggas till i
huvudexperimentet.
Pilotstudien genomfördes på en bärbar dator med operativsystemet Ubuntu Server (version
14.04.2 LTS). Hårdvarumässigt består datorn av en Intel Core 2 Duo processor på 1.33GHz
och 2GB RAM. Klientsidan använder webbläsaren Firefox (version 38.0.1) och tillägget
Greasemonkey (Greasemonkey, 2015). Anslutningen mellan klient och server utfördes över
ett lokalt nätverk med Wi-Fi.
14
4.3.2 Serverbelastning
Syftet med detta test var att ta reda på skillnaderna på serverbelastning hos Node jämfört
med Apache. Testet genomfördes med en användare i totalt 3 mätserier för varje webbserver,
där varje mätserie innefattade 100 iterationer, dvs. lägga till och ta bort en vara 100 gånger.
Därefter togs medelvärdena ut ifrån respektive mätserie och sammanställdes.
Serverbelastning
9,00%
8,00%
Millisekunder
7,00%
6,00%
5,00%
Apache
4,00%
Node.js
3,00%
2,00%
1,00%
0,00%
Figur 9
Jämförelse av serverbelastning
Mätresultaten i Figur 9 visar att Node.js har en serverbelastning som är mer än dubbelt så
hög jämfört med motsvarande mätresultat för Apache. Motsvarande mätningar har även
genomförts i huvudexperimentet i kommande kapitel, som även innefattar en djupare analys
kring mätresultaten av serverbelastningen. Det har även i experimentdelen utförts
mätningar på 8 respektive 16 användare och det är då intressant att jämföra hur stora
skillnaderna är på serverbelastningen mellan de två olika mätningarna.
15
4.4 Progression
En av de stora utmaningarna var att få sessionshanteringen att fungera korrekt. Sessioner i
webbläsaren är tillfällig information med lagrade variabler som webbservern har tillgång till
och sessionen försvinner när webbläsaren stängs. Detta fungerar enkelt i PHP med den
inbyggda funktionen $_SESSION. Problemet var att detta inte fanns som en fördefinierad
funktion till Node.js. Efter att ha testat en rad olika egenkomponerade lösningar verkade den
bästa lösningen finnas med tillägget Express-Session som fungerar på ett liknande sätt som
sessioner i PHP. I exemplet nedan illustreras hur en session skapas och en array läggs till.
var express = require("express");
var session = require("express-session");
var app = express();
app.use(session({secret: '1234abcd'}));
var session;
session.products = [
{ 'name': 'Android Phone FX1', 'code': 'PD1001', 'qty': 1, 'price': 200 },
{ 'name': 'Television DTX', 'code': 'PD1002', 'qty': 1, 'price': 500 },
{ 'name': 'External Hard Drive', 'code': 'PD1003', 'qty': 1, 'price': 100 },
{ 'name': 'Wrist Watch GE2', 'code': 'PD1004', 'qty': 1, 'price': 400 }];
Ett problem med hur Google Chrome fungerar gjorde att efter en vara tagits bort och lagts
till i kundvagnen senare inte gick att ta bort igen i Node.js-applikationen. Detta ledde till att
alla experiment därefter istället genomfördes med Firefox. Att Node.js är byggt på Google
Chromes JavaScript-motor borde innebära att Chrome är den webbläsare som är bäst
lämpad att exekvera Node.js-applikationer på. Därför hade det varit av intresse att
framförallt genomföra mätningarna i den webbläsaren.
Ett annat problem gjorde att intervallet ändrades i skriptet eftersom fönster med Node.js
låste sig när intervallet var 50 millisekunder, alltså för snabbt. Funktionen setInterval
exekverar funktionen inom ett visst intervall, i detta fall varje 5000 millisekund. Detta
intervall valdes efter ett antal tester och verkade vara det intervall som gjorde det möjligt att
funktionen hann exekveras och slutföras innan nästa iteration började. Att istället använda
setTimeout hade varit en lösning. Denna funktion gör att funktionen exekveras igen först när
den är helt klar. Av någon anledning gjorde detta att endast 4 samtidiga fönster kunde köras
samtidigt och de övriga låste sig. Av denna anledning användes setInterval som ett
komplement.
16
5 Utvärdering
I detta kapitel presenteras de mätningar som genomförts med tillhörande graf och en kort
diskussion. Mätningarna som gjordes i experimentdelen handlar om vilken inverkan de olika
webbservrarna, med respektive programmeringsteknik, har på svarstid och serverbelastning.
Slutsatser som kan dras ifrån pilotstudien är att antalet varor som läggs till inte har någon
större inverkan på svarstiden. Därför sker mätningarna på en vara som läggs till och tas bort.
5.1 Presentation av undersökning
Skriptet som beskrivs i pilotstudien om att lägga till och ta bort varor används även till
experimentet. Med tillägget Greasemonkey körs skriptet i Firefox där en ny profil skapats för
att på så sätt skapa enskilda processer som inte är beroende av varandra. Varje fönster
representerar en användare. Skriptet hade ett intervall på 500 millisekunder mellan varje
exekvering av klickfunktionen, dvs. 500 millisekunder mellan varje klick för att lägga till och
tömma kundvagnen. För att sedan starta skriptet för varje fönster samtidigt användes ett
verktyg för att klicka på fördefinierade koordinater på skärmen med ett intervall på några få
millisekunder emellan varje klick. Detta gjorde att alla fönster exekverade skriptet i stort sätt
samtidigt.
I experimentet genomfördes mätningar på svarstiden för 8 och 16 samtidiga användare.
Samtidigt genomfördes mätningar på serverbelastningen på servrarna. Samtliga experiment
bestod av 5 mätserier, 1000 iterationer och 8- respektive 16 användare.
Mätningarna genomfördes på en bärbar dator med operativsystemet Ubuntu Server (version
14.04.2 LTS). Hårdvarumässigt består datorn av en Intel Core 2 Duo processor på 1.33GHz
och 2GB RAM. Klientsidan använder webbläsaren Firefox (version 38.0.1) och tillägget
Greasemonkey. Anslutningen mellan klient och server utfördes över ett lokalt nätverk med
Wi-Fi.
17
5.1.1 Svarstid på server
Mätningarna på svarstid presenteras i grafer med 8 och 16 användare. Resultaten visar hur
hög svarstiden var på respektive server och svarstiden anges i millisekunder.
Standardavvikelsen visas även i graferna för att påvisa den genomsnittliga avvikelsen från
medelvärdet och alltså visa hur hög spridningen var bland mätvärdena.
Svarstid 8 och 16 användare
4
Millisekunder
3,5
3
2,5
2
Apache
1,5
Node.js
1
0,5
0
8 användare
16 användare
Figur 10 Svarstid – 8 och 16 användare
10
9
8
7
6
5
4
3
2
1
0
Svarstid 8 användare
Apache
Node.js
1
43
85
127
169
211
253
295
337
379
421
463
505
547
589
631
673
715
757
799
841
883
925
967
Millisekunder
Figur 10 visar mätresultatet för 8 och 16 samtidiga användare. Det första som är
anmärkningsvärt är att Node.js i båda mätningarma ligger lägre än Apache och det verkar
därför som att Node.js är bättre på att hantera anropen än Apache. Gribble, Brewer och
Culler (2000) menar att även när antalet anslutningar ökar med Node.js så är
genomströmningen den samma. Detta påstående verkar stämma överens med resultaten i
denna mätning.
Iterationer
Figur 11
Svarstid – 8 användare
18
Figur 11 för 8 användare visar ytterligare någonting som är anmärkningsvärt. I denna graf är
det tydligt att Apache har en rad höga spikar bland mätvärdena. Spikarna verkar vara relativt
konstanta och förekommer genom hela mätserien. Att det tar längre tid vid vissa punkter
kan bero på att Apaches trådhantering bidrar till att ankommande anrop läggs på kö istället
för att exekveras direkt. Alltså verkar det som att det maximala antalet anrop Apache kan
exekvera samtidigt snabbt förbrukas och bidrar med en längre exekveringstid för de
väntande anropen. På så sätt blir svarstiden högre för de mätvärden i grafen där spikarna
förekommer.
10
9
8
7
6
5
4
3
2
1
0
16 användare
Apache
Node.js
1
41
81
121
161
201
241
281
321
361
401
441
481
521
561
601
641
681
721
761
801
841
881
921
961
Millisekunder
Till mätningarna för 16 samtidiga användare bör det tilläggas att ett fåtal väldigt höga spikar
har kortats ner. Ett av mätvärdena låg så högt som 35 millisekunder och liknande höga
spikar har kortats ner till ett värde under 10 millisekunder. Detta gjordes för att tydligare
illustrera skillnaderna bland mätvärdena i grafen.
Iterationer
Figur 12 Svarstid – 16 användare
Figur 12 visar mätresultatet för 16 samtidiga användare. Likt mätresultaten för 8 användare
så ligger Node lägre än Apache. Node ligger på omkring 2 millisekunder vid varje mätvärde
medan Apache ligger omkring 3 millisekunder med ett antal höga spikar. Även spikarna är
alltså någonting som finns med i mätningarna med 16 användare. Dessa spikar är dock
nästan dubbelt så höga som för 8 användare. Förklaringen till detta kan vara att
belastningen blir högre och trådarna tar längre tid att exekveras eftersom antalet användare
dubblats jämfört med föregående experiment. Om detta mönster fortsätter att gälla kan det
vid ett större antal samtidiga användare bli ett större problem.
19
5.1.2 Serverbelastning
I samband med att mätningarna för svarstid gjordes så genomfördes även mätningar på hur
hög serverbelastningen var angett i procent. Linux-kommandot top användes för att logga
hur hög serverbelastningen var vid olika mätpunkter som loggades var tredje sekund
(appendix B).
Serverbelastning
50,00%
45,00%
40,00%
35,00%
30,00%
25,00%
20,00%
15,00%
10,00%
5,00%
0,00%
Apache
Node.js
8 användare
16 användare
Figur 13 Serverbelastning – 8 användare
Mätresultaten för serverbelastningen i pilotstudien visade att Node.js har en högre
serverbelastning än Apache. Figur 13 visar att experimentet på serverbelastningen för 8 och
16 användare resulterade i ett liknande resultat. Grafen visar att ökningen för Node.js mellan
8 och 16 användare är ungefär 10% medan det ligger på ungefär 3% för Apache. Ökningen
för Node.js är alltså signifikant högre än Apache och sker ökningen exponentiellt när antalet
användare dubbleras ytterligare kan serverbelastningen komma att bli ett problem med
Node.js. Gribble, Brewer, och Culler (2000) menar att flaskhalsen är processorn på
webbservern och är processorn fullt belastad kan en applikation agera långsamt eller i värsta
fall resultera i att servern kraschar.
20
5.1.3 Loggning på server och kodfil
I detta kapitel analyseras hur stora skillnaderna är mellan server och kodfil samt hur de
förhåller sig till varandra. Mätvärdena som visas i figur 14 är tiden det tog att exekvera
mätskriptet på serversidan samt att exekvera kodfilen på servern.
Server och kodfil
3,5
Millisekunder
3
2,5
2
1,5
Server
1
Kodfil
0,5
0
Node.js
Apache & PHP
8 användare
Node.js
Apache & PHP
16 användare
Figur 14 Svarstid för klient och kodfil
Figur 14 visar en sammanställning av tiden som loggats på server och i kodfil. PHP-kod
exekveras väldigt snabbt och utgör endast en liten del i den totala exekveringstiden. Node.jskod exekveras däremot med en högre svarstid och verkar alltså inte vara lika optimerat som
PHP-kod. Hade Node.js-koden kunnat optimeras till att få en liknande exekveringstid som
PHP så hade Node.js blivit en ännu snabbare lösning. Den tydligaste skillnaden är hur
mycket svarstiderna på serversidan skiljer sig åt. Apache är den främsta anledningen till att
PHP-applikationen har en högre svarstid än Node.js.
21
5.2 Analys och slutsatser
Mätresultaten för svarstid på server visade att svarstiden för Node.js är lägre än Apache. Ett
stort antal spikar förekommer dessutom med Apache som beror på trådhanteringen Apache
är baserad på. Anrop som väntar på att föregående anrop ska exekveras läggs på kö och det
är dessa anrop som motsvarar spikarna i mätresultaten. Bosak & Zakova (2015) visar i sina
mätningar att Node.js som webbserver är en optimal lösning som bidrar till korta svarstider.
Anledningen till att Node.js som webbserver är så snabb har att göra med att Node.js är
byggd på Google Chromes JavaScript motor (V8). Bosak & Zakova (2015) menar även att en
ytterligare fördel är att Node.js som är programmerat i JavaScript är en ideal lösning för
asynkron hantering av anrop. Node.js slipper den ofta problematiska trådhanteringen som
exempelvis finns i Apache. Detta styrker mätresultaten som utförts i detta arbete och det kan
sägas att Node.js alltså verkar vara en bättre optimerad webbserver än Apache.
Mätresultaten för serverbelastningen visade att Node.js har en större belastning på server än
Apache. Dessutom bidrog Node.js till en ökning på 10% mellan 8 och 16 användare där
Apache endast ökade 3%. Gribble, Brewer, och Culler (2000) menar att flaskhalsen är
processorn på webbservern och en hög serverbelastning kan leda till att en webbapplikation
eller server kraschar. Node.js verkar vara en bättre i fråga om svarstid men det är också
viktigt att ha serverbelastningen i åtanke vid ett val av vilken webbserver som ska användas.
Node.js hanterar anropen både snabbare och bättre än Apache utan att anrop läggs på kö.
Men detta medför också att serverbelastningen blir högre på Node.js och i de fall CPU är en
ekonomisk fråga kan det eventuellt löna sig att istället använda Apache. Griffin, Butler, de
Leastar, Jennings & Botvich (2012) visar i sina mätningar att Node.js har en effektivare
användning av CPU för att exempelvis hantera det nästkommande anropet eller att
förbereda ett svar. Detta kan förklara varför Node.js har en relativt hög serverbelastning
jämfört med Apache. Nackdelarna med Node.js som server blir givetvis en högre belastning
på CPU medan fördelarna blir en snabbare och smidigare hantering av anrop.
Slutligen genomfördes en jämförelse mellan tiden som loggades på klient och i kodfilen på
de två applikationslösningarna. Denna jämförelse visade att PHP-kod exekveras mycket
snabbare än Node.js-kod samt att Apache verkar vara det som har störst negativ inverkan på
svarstiden för PHP/Apache-lösningen. Den generella slutsatsen som kan dras av
mätresultaten i experimentet är alltså att Node.js fungerar bättre som webbserver än
Apache, PHP-kod exekveras snabbare än Node.js-kod och att Node.js har en högre
serverbelastning än Apache.
22
6 Avslutande diskussion
6.1 Sammanfattning
Arbetet handlade om att påvisa skillnader mellan Node.js och Apache där mätningar på
svarstid samt serverbelastning utförs. Mätresultaten för experimentet på svarstid visar att
Node.js har en konstant låg svarstid. Apache däremot har högre svarstid där en mängd
spikar också förekommer i mätningarna. Spikarna tyder på att Apache inte kan hantera
anropen lika väl som Node.js. Detta beror på att Apache är trådbaserat och då ett flertal
anrop samtidigt ankommer till servern läggs anrop på kö i väntan på att föregående anrop
ska exekveras. Skillnaderna på svarstider mellan 8 och 16 användare är tydlig. Spikarna för
16 användare är nästan dubbel så höga jämfört med experimentet för 8 användare.
Experimentet som mätte serverbelastningen visade att Node.js har en högre belastning på
CPU än Apache. Skillnaderna är stora då Node.js har mer än dubbelt så hög belastning än
Apache. Hög belastning på servern kan resultera i att en applikation agerar långsamt eller att
servern kraschar. Det som överensstämde med hypotesen var att svarstiderna skulle vara
lägre med Node.js och det som motbevisades var att serverbelastningen var mycket högre än
förväntat.
6.2 Diskussion
Som det nämndes i kapitel 3 så har syftet med detta arbete varit att undersöka skillnaderna
på svarstid och serverbelastning mellan Node.js och Apache som webbserver. Mätningarna
utfördes med två kundvagnsapplikationer utvecklade i JavaScript och PHP. Applikationen
utvecklad i JavaScript exekverades på Node.js och den PHP-utvecklade exekverades på
Apache. Parametrarna som mätningarna utfördes på var alltså serverbelastning och svarstid.
Mätningarna utfördes som experiment eftersom att vetenskapligt framtagen programvara
enligt Basili (1996) grundar sig i att experiment använts under utvecklingsprocessen.
Eftersom samtidiga användare har varit en parameter i mätningarna så skapades ett skript
för att simulera klick på applikationen. Att istället utföra mätningar med riktiga användare
hade varit ett alternativ eftersom det är just riktiga användare som i slutändan kommer
använda kundvagnsapplikationerna.
Experimentet visade med mätningarna på svarstid att Node.js konstant har en lägre svarstid
än Apache. En annan stor nackdel var att det med Apache förekom en rad spikar i
mätresultaten som förklaras med att det är vid de punkterna anrop läggs på kö p.g.a.
Apaches trådhantering. Skillnaderna med 8 och 16 användare var inte direkt märkbar med
Node.js som vid båda mätningar höll ett stabilt intervall för anropen. Apache hanterade
däremot inte det olika antalet användare lika bra. Vid 16 användare visade Apache att
svarstiden blev ännu högre när anrop lades på kö. Detta visar att de händelsebaserade
interaktionerna Node.js använder bidrar till en jämnare och stabilare hantering av anrop.
För att styrka detta påstående så menar Gribble, Brewer och Culler (2000) att även när
antalet anslutningar ökar med Node.js så är genomströmningen den samma, vilket stämmer
överens med mätresultaten i detta arbete.
Mätresultaten för serverbelastningen visade att Node.js ligger relativt högt jämfört med
mätresultaten för Apache. Ökningen mellan mätningarna med 8 och 16 användare var att
Node.js serverbelastning ökade med 10% medan Apache endast ökade med 3%. Griffin,
23
Ryan, De Leastar & Botvich (2011) visar i sina mätningar att deras Node.js-applikation
använde hela 100% av de tillgängliga CPU-resurserna. Den höga belastningen berodde på att
hanteringen av events krävde stora CPU-resurser för att få en bättre genomströmning. De
nämner också att en bättre genomströmning var en vinst som gjorde att den höga
serverbelastningen var en värdefull uppoffring.
Griffin, Butler, de Leastar, Jennings & Botvich (2012) visar i sina mätningar att Node.js har
en högre serverbelastning på grund av att nästkommande anrop hanteras eller att ett svar
förbereds. Detta tyder på att Node.js har en hög belastning på servern där fördelen är en
smidare hantering av anrop.
Arbetet visade att fördelarna med Node.js verkar vara att svarstiderna blir lägre med
nackdelen att serverbelastningen är hög och ökar vid fler användare. Hypotesen var att en
kundvagnsapplikation skriven i JavaScript och exekverad på Node.js kommer ha lägre
serverbelastning och svarstider jämfört med samma applikation skriven i PHP och
exekverad på Apache. Det som bevisades var att svarstiden med Node.js var lägre medan
serverbelastningen var mycket högre.
Samhällelig nytta hos arbetet är att göra valet enklare av vilket programmeringsspråk och
webbserver som ska användas till en kundvagnsapplikation. Resultaten i arbetet visar att
Node.js framförallt verkar vara en snabb webbserver som kan hantera ett större antal
användare utan att svarstiden påverkas. Att migrera en PHP/Apache-baserad applikation till
Node.js är någonting som verkar vara en värdefull investering för exempelvis en e-butik. En
viktig aspekt är dock att Node.js har en högre serverbelastning och det är därför viktigt att ha
med detta i beräkningen och reflektera kring om det blir lönsamt att övergå till att använda
Node.js.
Ur ett forskningsetiskt perspektiv är det viktigt att möjliggöra en återupprepning av
experiment. Alla programverktyg som använts i arbetet har varit öppen källkod och/eller
gratis att använda. Även den programkod som skapats finns tillgänglig i appendix.
Konfigurationer för varje programvara samt hårdvara anges. Mätresultaten publiceras i
sammanställd form i grafer. Inga personuppgifter eller liknande har använts i arbetet och
därför är etiska aspekter av denna sort ingenting som innefattas i arbetet.
6.3 Framtida arbete
Eftersom serverbelastningen var hög med Node.js kan det till ett framtida arbete användas
bättre hårdvara för att sätta upp en riktig server som klarar en högre serverbelastning istället
för att låta en laptop fungera som server. Mätningarna sker över ett lokalt nätverk och det
hade kunnat vara intressant att istället mäta över ett globalt nätverk som är mer instabilt och
ta reda på hur svarstiderna påverkas. Då blir även nätverksfördröjningen en parameter som
bör loggas i mätningarna.
Ett samarbete med ett företag hade bidragit till att en fallstudie hade kunnat utföras och
även för att nå verkliga användare. Användarna kan vara de anställda på företaget som
agerar kunder och använder applikationen. Detta hade varit en alternativ lösning till att
simulera användare med ett skript. Mätningarna kan då även innefatta flera hundra
användare istället. Applikationen kan sedan komma att användas av företaget och bidrar då
till samhällelig nytta på företaget.
24
I mätningarna användes ett intervall på 500 millisekunder vilket gjorde att varje mätserie
tog ungefär lika lång tid för både Node.js och Apache. Det hade kunnat vara av intresse att
mäta på vilken av servrarna som exekverar exempelvis 1000 iterationer snabbast. Alltså att
ta reda på vilken server som kan göra flest anrop inom ett visst intervall.
Node.js visade att med mätningarna mellan 8 och 16 användare ökade serverbelastningen
10%. Mätningar hade kunnat utföras för att ta reda på om serverbelastningen fortsätter att
öka med ännu fler användare.
Både Node.js och Apache är baserade på http-protokollet och detta är någonting som har en
inverkan på svarstiderna på servern. En websocket-lösning använder inte http-protokollet
och en sådan lösning hade därför kunnat minska svarstiderna märkvärt. Därför hade en
websocket-lösning varit av intresse att implementera för att ta reda på om och i så fall hur
mycket lägre svarstiderna blir för applikationen. Figur 14 i kapitel 5.1.3 visar hur stor del
servern och kodfilen tar upp av den totala tiden på klientsidan. Med en websocket-lösning
hade stapeln som representerar servertiden eventuellt haft en lägre svarstid. Dessutom hade
en mätning mellan Node.js, Apache och websocket-lösningen gjort det tydligt att påvisa
skillnaderna mellan de olika lösningarna.
25
Referenser
Apache. (2014, 18 mars). Apache Performance Tuning [Dokumentation]. Hämtad från
http://httpd.apache.org/docs/2.4/misc/perf-tuning.html
Basili, V. R. (1996). The role of experimentation in software engineering: past, current, and
future. Proceedings of the 18th international conference on Software engineering.
Berlin, Tyskland, IEEE. s. 442-449.
Behrends, R., Stirewalt, R. K., & Dillon, L. K. (2005). A component-oriented model for the
design of safe multi-threaded applications. Component-Based Software Engineering, s.
251-266.
Bell, C. (2012). MySQL and The Open Source Revolution. Expert MySQL, s. 3-21.
Bosak, T., & Zakova, K. (2015, februari). Node. js based remote control of thermo-optical
plant. Remote Engineering and Virtual Instrumentation (REV), 2015 12th International
Conference, IEEE. s. 209-213.
Cao, J., Andersson, M., Nyberg, C., & Kihl, M. (2003). Web server performance modeling
using an M/G/1/K* PS queue. 10th International Conference on Telecommunications,
2003. Tahiti, Papeete, Franska Polynesien, IEEE. s 1501-1506.
Close, A. G., & Kukar-Kinney, M. (2010). Beyond buying: Motivations behind consumers'
online shopping cart use. Journal of Business Research, 63(9), 986-992.
Davarzani, H., & Norrman, A. (2015). Toward a relevant agenda for warehousing research:
literature review and practitioners’ input. Logistics Research, 8(1), 1-18.
Gribble, S. D., Brewer, E. A., & Culler, D. (2000). A design framework for highly concurrent
systems. Berkeley: University of California, Computer Science Division.
Griffin, L., Butler, B., de Leastar, E., Jennings, B., & Botvich, D. (2012, juli). On the
performance of access control policy evaluation. Policies for Distributed Systems and
Networks (POLICY), 2012 IEEE International Symposium on, IEEE. s. 25-32.
Griffin, L., Ryan, K., De Leastar, E., & Botvich, D. (2011, juni). Scaling Instant Messaging
communication services: A comparison of blocking and non-blocking techniques.
Computers and Communications (ISCC), 2011 IEEE Symposium, IEEE. s. 550-557.
Griffin, L., Elger, P., & de Leastar, E. (2013). Project Zeppelin: A Modern Web Application
Development Framework. Lecture notes in computer science, 7542, s. 184-198.
Hills, M., Klint, P., & Vinju, J. (2013). An empirical study of PHP feature usage: a static
analysis perspective. Proceedings of the 2013 International Symposium on Software
Testing and Analysis, s. 325-335.
Khanuja, H. K., & Adane, D. S. (2012). A framework for database forensic analysis. Computer
Science & Engineering: An International Journal (CSEIJ), s. 27-41.
Machlis, S. (2002). PHP. Computerworld, vol. 36 nummer 6 s. 46.
26
Nodejs.org. [Dokumentation]. Hämtad från https://www.nodejs.org
Ojamaa, A., & Duuna, K. (2012). Assessing the security of Node. js platform. 2012
International Conference for Internet Technology And Secured Transactions. London,
England, IEEE. s. 348-355.
Portokalidis, G., & Keromytis, A. D. (2010). Fast and practical instruction-set randomization
for commodity systems. Proceedings of the 26th Annual Computer Security Applications
Conference. Austin, Texas, USA, ACM. s. 41-48.
Sanwebe.com, (2015, 16 februari). Creating Simple Shopping Cart with PHP
[Dokumentation]. Hämtad från http://www.sanwebe.com/2013/06/creating-simpleshopping-cart-with-php
StackOverflow (2012) How to get overall CPU usage on Linux. Tillgänglig på Internet:
http://stackoverflow.com/questions/9229333/how-to-get-overall-cpu-usage-e-g-57-onlinux [Hämtad 11 maj 2015].
Stober, T., Hansmann, U. (2010). Agile software development : best practices for large
software development projects [Springer-version].
Temme, S. (2007). Apache performance tuning part one: scaling up. ApacheCon US, s. 12-17.
Tilkov, S., & Vinoski, S. (2010) Node.js: Using JavaScript to build high-performance network
programs.
IEEE
Internet
Computing,
14(6),
s.
80-83.
27
Appendix A - Skript för svarstid
// ==UserScript==
// @match
192.168.0.106
// @version
1
// @grant
none
// ==/UserScript==
// ==UserScript==
var d = new Date();
window.onload = function () {
startBtn();
printClient();
}
//Press esc to stop script
window.addEventListener("keyup", function(e){ if(e.keyCode == 27)
localStorage.setItem("index", null); }, false);
function startTest(){
setInterval(function () {
var start = localStorage.getItem("start");
var index = localStorage.getItem("index");
var iterations = localStorage.getItem("iterations");
var items = localStorage.getItem("items");
var itemNum = localStorage.getItem("itemNum");
index = parseInt(index);
iterations = parseInt(iterations);
itemNum = parseInt(itemNum);
setTimeout(function () {
if(index < iterations){
if(itemNum < items){
startTimer();
document.getElementById('addToCart'+itemNum).click();
if(document.getElementById('loopa').value="1"){ //Value is set by
code running on server to tell client when to stop logging
stopTimer();
setTime();
}
itemNum++;
localStorage.setItem("itemNum", itemNum);
}
else{
removeCart();
localStorage.setItem("itemNum", 0);
index++;
localStorage.setItem("index", index);
}
}
}, 175);
}, 500);
}
startTest();
function removeCart(){
I
setInterval(function () {
document.getElementById('emptycart').click();
}, 100);
}
function startBtn(){
var button = document.createElement('button');
button.innerHTML = 'Start';
button.onclick = function(){
var text = prompt("Enter iterations");
if (text != null) {
var items = prompt("Enter items");
localStorage.setItem('iterations', text);
localStorage.setItem('items', items);
localStorage.setItem("index", 0);
localStorage.setItem('start', 1);
if(window.location.href == "http://192.168.0.106/php/" ||
window.location.href == "http://192.168.0.106:8080/"){
location.reload();
startTest();
}
else{
alert("Go back to start page");
}
location.reload();
}
};
document.body.appendChild(button);
}
localStorage.stopTime;
localStorage.startTime;
function printClient(){
var button = document.createElement('button');
button.innerHTML = 'Print log';
button.onclick = function(){
var times = localStorage.getItem('times');
var div = document.createElement('div');
div.innerHTML = times;
document.body.appendChild(div);
}
document.body.appendChild(button);
}
function removeItem(){
setInterval(function () {
document.getElementById('removeitem').click();
localStorage.setItem('start', 4);
}, 500);
}
function resetCart(){
setInterval(function () {
location.reload();
II
localStorage.clear();
localStorage.setItem('start', 1);
}, 500);
}
function printTime(){
var jump = "Start: "+localStorage.startTime+" Stop:"+localStorage.stopTime;
var objects1 = JSON.parse(localStorage.getItem("startTime"))[0];
var objects2 = JSON.parse(localStorage.getItem("stopTime"))[0];
console.log("Game of "+objects1+" : "+objects2);
}
function resetBtn(){
var button = document.createElement('button');
button.innerHTML = 'Reset';
button.onclick = function(){
localStorage.clear();
};
document.body.appendChild(button);
}
function timeBtn(){
var button = document.createElement('button');
button.innerHTML = 'Get time';
button.onclick = function(){
getTime();
};
document.body.appendChild(button);
}
function startTimer(){
var time = performance.now();
localStorage.setItem('startTimer', time);
}
function stopTimer(){
var time = performance.now();
localStorage.setItem('stopTimer', time);
}
function setTime(){
var start = localStorage.getItem('startTimer');
var end = localStorage.getItem('stopTimer');
var interval = (end - start);
var times = localStorage.getItem('times');
if(times){
times += ",";
}
times += interval;
localStorage.setItem('times', times);
}
III
Appendix B - Skript för serverbelastning
IV
Appendix C - Pilotstudie, antal varor med Apache
Antal varor - Apache
8
7
16 varor
5
8 varor
4
4 varor
3
2 varor
1 vara
2
1
0
1
5
9
13
17
21
25
29
33
37
41
45
49
53
57
61
65
69
73
77
81
85
89
93
97
Millisekunder
6
Interaktioner
V
Appendix D - Pilotstudie, antal varor med Node
Antal varor - Node
8
7
16 varor
5
8 varor
4
4 varor
3
2 varor
1 vara
2
1
0
1
5
9
13
17
21
25
29
33
37
41
45
49
53
57
61
65
69
73
77
81
85
89
93
97
Millisekunder
6
Interaktioner
VI
Appendix E - Node-applikation
var stopLog;
var express = require("express");
var session = require("express-session");
var app = express();
var mysql = require("mysql");
var bodyParser = require('body-parser');
var client = mysql.createConnection({
"host": "localhost",
"port": 3306,
"user": "root",
"password": "manthesven",
"database" : "demo"
});
/*------------------------------------------------------------------------------------------------------------------- Equivalent to index.php ----------------------------------------------------------------------------------------------------------------------*/
app.use(session({secret: '1234abcd'}));
app.use(bodyParser());
app.use(express.static(__dirname + '/')); //Specify root folder to link to CSS
folder
var loopa; //Variable for session, log/measuring purpose
var sess; //Variable for session ($_SESSION in PHP)
app.get('/printLog', function (req, res) {
res.write('<p>'+req.session.interval+'</p>');
res.end();
});
app.get('/resetLog', function (req, res) {
req.session.destroy;
req.session.interval = "";
res.end();
});
/*------------------------------------------------------------------------------------------------------------------- Equivalent to index.php ----------------------------------------------------------------------------------------------------------------------*/
app.get("/",function(req,res){
//Start logging when document loads
var startLog = process.hrtime();
//If item is added to cart, i.e. POST-data sent
if(req.session.loopa == 1){
stopLog = process.hrtime(startLog); //use startLog as argument to get the
interval
res.write("<div id='loopa' value='"+req.session.loopa+"'></div>");//Change
value on div to tell the client when to stop log as well
req.session.loopa = 0;
}
VII
else if(req.session.loopa == 0){
res.write("<div id='loopa' value='"+req.session.loopa+"'></div>");//Change
value on div to tell the client when to stop log as well
}
//Do not log time if 'empty cart' is pressed
if(req.session.notLog == 1){
req.session.notLog = 0;
}
else{
req.session.interval += " "+stopLog;
}
var current_url = req.originalUrl;
var sql = "SELECT * FROM products ORDER BY id ASC";
var i = 0;
client.query(sql, [], function(err, results, fields) {
if(results){
res.write("<html><head><title>Shopping Cart</title><link
href='style/style.css' rel='stylesheet' type='text/css'></head><body>");
res.write("<div id='products-wrapper'>");
res.write("<h1>Products</h1>");
res.write("<p id='timeStamp'></p>");
res.write("<div class='products'>");
for(obj in results)
{
res.write("<div class='product'>");
res.write("<form method='post' action='/cart_update'>");
res.write("<div class='product-thumb'><img
src='images/"+results[obj].product_img_name+"'></div>");
res.write("<div class='productcontent'><h3>"+results[obj].product_name+"</h3>");
res.write("<div class='productdesc'>"+results[obj].product_desc+"</div>");
res.write("<div class='product-info'>");
res.write("Price $"+results[obj].price+" | ");
res.write("Qty <input type='text' name='product_qty' value='1'
size='3' />");
res.write("<button class='add_to_cart' id='addToCart"+i+"'>Add To
Cart</button>");
res.write("</div></div>");
res.write("<input type='hidden' name='product_code'
value='"+results[obj].product_code+"' />");
res.write("<input type='hidden' name='type' value='add' />");
res.write("<input type='hidden' name='return_url'
value='"+current_url+"' />");
res.write("</form>");
res.write("</div>");
i++;
}
res.write("</div>");
res.write("<div class='shopping-cart'>");
res.write("<h2>Your Shopping Cart</h2>");
if(sess)
{
var total = 0;
VIII
res.write("<ol>");
for(cart_itm in sess)
{
res.write('<li class="cart-itm">');
res.write('<span class="remove-itm"><a
href="/cart_update?removep='+sess[cart_itm].code+'&return_url='+current_url+'"
id="removeitem">&times;</a></span>');
res.write('<h3>'+sess[cart_itm].name+'</h3>');
res.write('<div class="p-code">P code :'+sess[cart_itm].code+'
</div>');
res.write('<div class="p-qty">Qty : '+sess[cart_itm].qty+'</div>');
res.write('<div class="p-price">Price
:'+sess[cart_itm].price+'</div>');
res.write('</li>');
var subtotal = (sess[cart_itm].price*sess[cart_itm].qty);
var total = (total + subtotal);
}
res.write("</ol>");
res.write('<span class="check-out-txt"><strong>Total : $'+total+'</strong>
<a href="/view_cart">Check-out!</a></span>');
res.write('<span class="empty-cart"><a
href="/cart_update?emptycart=1&return_url='+current_url+'" id="emptycart">Empty
Cart</a></span>');
}else{
res.write("Your Cart is empty");
}
res.write("</div>");
//Logging buttons
res.write('<div style="float: left">');
res.write('<a href="/printLog" id="printLog">Print server log</a>&nbsp;');
res.write('</div>');
res.write("</div>");
res.write("</div>");
res.write("</body>");
res.write("</html>");
res.end();
}
});
});
/*---------------------------------------------------------------------------------------------------------------- Equivalent to cart_update.php ------------------------------ (POST data) ------------------------------------------------------------------------*/
app.post('/cart_update', function(req, res){
if(sess === 'undefined'){ //Sets session if it doesn't already exists
sess = req.session.products = [];
}
var product_code = req.body.product_code;
var product_qty = req.body.product_qty;
if(product_qty > 10){//Error page for too many products
IX
res.write('<div align="center">This demo does not allowed more than 10
quantity!<br /><a href="/">Back To Products</a>.</div>');
res.end();
}
else{
var sql = "SELECT product_name, price, product_desc FROM products WHERE
product_code='"+product_code+"' LIMIT 1";
client.query(sql, [], function(err, results, fields) {
if(results){
var product = [];
for(obj in results){
var new_product = [];
new_product.push({
name: results[obj].product_name,
code: product_code,
qty: product_qty,
price: results[obj].price,
desc: results[obj].product_desc
});
}
if(sess){
var found = false;
for(cart_itm in sess){
if(sess[cart_itm].code == product_code){
product.push({
name: sess[cart_itm].name,
code: sess[cart_itm].code,
qty: product_qty,
price: sess[cart_itm].price,
desc: sess[cart_itm].desc
});
found = true;
}else{
product.push({
name: sess[cart_itm].name,
code: sess[cart_itm].code,
qty: sess[cart_itm].qty,
price: sess[cart_itm].price,
desc: sess[cart_itm].desc
});
}
}
if(found == false){
sess = product.concat(new_product);
}else{
sess = product;
}
}else{
sess = new_product;
}
}
});
req.session.loopa = 1;
X
//redirect back to original page
res.writeHead(301,
{Location: '/'}
);
res.end();
}
});
/*---------------------------------------------------------------------------------------------------------------- Equivalent to cart_update.php ------------------------------- (GET data) ------------------------------------------------------------------------*/
app.get('/cart_update', function (req, res) {
var return_url = req.query.return_url;
var emptycart = req.query.emptycart;
if(emptycart && emptycart == 1){//Empty cart by destroying current session
sess = null;
}
if(sess){
var product_code = req.query.removep;
var product = [];
for(cart_itm in sess){
if(sess[cart_itm].code != product_code){
product.push({
name: sess[cart_itm].name,
code: sess[cart_itm].code,
qty: sess[cart_itm].qty,
price: sess[cart_itm].price,
desc: sess[cart_itm].desc
});
}
}
sess = product;
}
//Prevents logging when 'empty cart' is pressed
req.session.notLog = 1;
//Redirect back to original page
res.writeHead(301,
{Location: return_url}
);
res.end();
});
/*---------------------------------------------------------------------------------------------------------------- Equivalent to view_cart.php -------------------------------------------------------------------------------------------------------------------*/
app.get('/view_cart', function (req, res) {
console.log("View cart");
res.write("<html><head><title>View shopping cart</title><link
href='style/style.css' rel='stylesheet' type='text/css'></head><body>");
res.write("<div id='products-wrapper'>");
res.write("<h1>View Cart</h1>");
XI
res.write("<div class='view-cart'>");
var current_url = req.originalUrl;
if(sess){
var total = 0;
res.write("<form method='post' action='paypal-express-checkout'>");
res.write("<ul>");
var cart_items = 0;
for(cart_itm in sess){
var product_code = sess[cart_itm].code;
res.write("<li class='cart-itm'>");
res.write('<span class="remove-itm"><a
href="/cart_update?removep='+sess[cart_itm].code+'&return_url='+current_url+'"
id="removeitem">&times;</a></span>');
res.write("<div class='pprice'>$"+sess[cart_itm].price+"</div>");
res.write("<div class='product-info'>");
res.write("<h3>"+sess[cart_itm].name+" (Code
:"+product_code+")</h3>");
res.write("<div class='p-qty'>Qty :
"+sess[cart_itm].qty+"</div>");
res.write("<div>"+sess[cart_itm].desc+"</div>");
res.write("</div>");
res.write("</li>");
var subtotal = (sess[cart_itm].price*sess[cart_itm].qty);
var total = (total + subtotal);
res.write('<input type="hidden"
value="'+sess[cart_itm].name+'" />');
res.write('<input type="hidden"
value="'+product_code+'" />');
res.write('<input type="hidden"
value="'+sess[cart_itm].desc+'" />');
res.write('<input type="hidden"
value="'+sess[cart_itm].qty+'" />');
name="item_name['+cart_items+']"
name="item_code['+cart_items+']"
name="item_desc['+cart_items+']"
name="item_qty['+cart_items+']"
cart_items++;
}
res.write('</ul>');
res.write('<span class="check-out-txt">');
res.write('<strong>Total : $'+total+'</strong>');
res.write('</span>');
res.write("</form>");
}else{
res.write('Your Cart is empty');
}
res.write("</div>");
res.write("</div");
res.write("</body>");
res.write("</html>");
res.end();
});
app.listen(8080);
XII