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">×</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> '); 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">×</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
© Copyright 2024