onsdag 6. juli 2011 Node.js
Node.js er en cutting edge plattform som skal gjøre det enkelt, eller i alle fall enklere, å lage skalerbare nettverksprogrammer. Den er bygget på toppen av V8, Googles JavaScript-motor som kompilerer JavaScript til maskinkode før den kjøres (i motsetning til å kompilere til bytekode, eller tolke koden).
I motsetning til de fleste, tilsvarende plattformene baserer ikke Node.js sin skalerbarhet på tråding. Node-kode består i å knytte en rekke callbacks opp mot events, og så sover programmet til et av disse eventene inntreffer. Hvert event – f.eks. hver connection i en webserver – bruker veldig lite minne, og blokkerer aldri prosessen. Dessuten er dead-locks helt umulig, for siden det ikke finnes tråder finnes det heller ingen locks.
Som et første eksepriment med Node.js har jeg laget en mini-website som lister opp mine Intouch-kontakter. PSWinCom Intouch er en webbasert klient for mobil kommunikasjon. Den eksponerer et REST-api som gjør at jeg blant annet kan hente ut informasjon fra kontoen min i XML eller json-format.
Først lager jeg en statisk html-fil som skal vise kontaktene. Den bruker litt jQuery-magi for å hente kontaktene fra Node.js-serveren og viser dem i siden. Jeg kunne selvsagt generert html’en for kontaktene på server-siden, men synes egentlig det er bedre å bruke en Ajax-løsning.
UML-diagrammet over viser kommunikasjonsflyten. Jeg har også forsøkt å illustrere forskjellen på en Node.js-basert server og en vanlig webserver som Intouch-serveren representerer; Node.js blokkerer ikke, men er bare aktiv når den har noe å gjøre.
index.html ser ut som dette:
1 <html> 2 <head> 3 <title>My Intouch Contacts</title> 4 <script type="text/javascript" src="jquery.min.js"></script> 5 <script type="text/javascript"> 6 7 var loadContacts = function() { 8 var formatContact = function(contact) { 9 return $("<div>").append( 10 "<b>Name:</b> " + contact.Firstname + 11 " " + contact.Lastname + 12 " <b>Phone:</b> " + contact.PhoneNumber); 13 }; 14 $.getJSON("/contacts", function(contacts) { 15 $.each(contacts, function() { 16 $("#contacts").append(formatContact(this)); 17 }); 18 }); 19 } 20 21 $(function() { 22 loadContacts(); 23 }); 24 </script> 25 </head> 26 <body> 27 <h1>Intouch Contacts</h1> 28 <div id="contacts"></div> 29 </body> 30 </html>
Serveren jeg lager må altså både kunne levere ut index.html og sende kontaktene. Her følger server-koden i sin helhet. Merk at jeg er helt n00b i Node.js, og har klippet og limt litt fra diverse eksempler for å få dette til å virke. Følgende kildekode lagret jeg i en fil jeg kalte server.js.
1 var sys = require("sys"), 2 http = require("http"), 3 url = require("url"), 4 path = require("path"), 5 fs = require("fs"), 6 rest = require("restler"); 7 8 function load_static_file(uri, response) { 9 var filename = path.join(process.cwd(), uri); 10 path.exists(filename, function(exists) { 11 if(!exists) { 12 response.writeHeader(404, {"Content-Type": "text/plain"}); 13 response.write("404 Not Found\n"); 14 response.close(); 15 return; 16 } 17 18 fs.readFile(filename, "binary", function(err, file) { 19 if(err) { 20 response.writeHead(500, {"Content-Type": "text/plain"}); 21 response.end(err + "\n"); 22 return; 23 } 24 25 response.writeHeader(200); 26 response.end(file, "binary"); 27 }); 28 }); 29 } 30 31 http.createServer(function(request, response) { 32 var uri = url.parse(request.url).pathname; 33 if(uri === "/") { 34 load_static_file("index.html", response); 35 return; 36 } 37 else if(uri === "/contacts") { 38 rest.get("http://intouchapi.pswin.com/1/contacts", { 39 username: 'myuser@mydomain', 40 password: 'mypassword', 41 }).on('complete', function(data) { 42 response.writeHeader(200, { "Content-Type" : "application/json" }); 43 response.end(JSON.stringify(data)); 44 }); 45 } 46 else { 47 console.log("Not supported request " + uri); 48 response.writeHeader(404, {"Content-Type": "text/plain"}); 49 response.end("404 Not Found\n"); 50 } 51 }).listen(8080); 52 53 sys.puts("Server running at http://localhost:8080/");
Linje 8 til 29 er kun en funksjon for å serve statiske filer, og det aner meg at det finnes enklere måter å gjøre dette på. Selve serveren registreres på linje 31, hvor jeg legger opp til tre muligheter…
Hvis requesten er “/”, leverer jeg ut index.html-filen. Hvis requesten er “/contacts” gjør jeg et REST-kall til Intouch, og når jeg får svar tilbake derfra sender jeg det bare videre. For alle andre requester svarer jeg tilbake med status 404 Not Found.
Og det var det! Når jeg kjører kommandoen “node server.js” i konsollet, og deretter åpner opp http://localhost:8080/ i en browser, får jeg et resultat som ser ut omtrent som dette:
Name: Ola Nordman Phone: 90807060
Name: Kari Nordman Phone: 90807061
Name: Thor Divel Phone: 90807062
Name: Bob Kåre Phone: 90807063
Mulige utvidelser av denne mini-siten, som vil være ganske enkelt å få til, kan for eksempel være å gi brukeren mulighet til å trykke på en kontakt for å sende ham/henne en SMS-melding. Jeg kunne også ha listet ut kontakt-gruppene mine fra Intouch, og gitt mulighet for å distribuere en SMS til hele gruppen. Mulighetene er mange!