• Save
NodeJS: the good parts? A skeptic’s view (jax jax2013)
Upcoming SlideShare
Loading in...5
×
 

NodeJS: the good parts? A skeptic’s view (jax jax2013)

on

  • 10,133 views

JavaScript used to be confined to the browser. But these days, it's becoming increasingly popular in server-side applications in the form of Node.js. Node.js provides event-driven, non-blocking I/O ...

JavaScript used to be confined to the browser. But these days, it's becoming increasingly popular in server-side applications in the form of Node.js. Node.js provides event-driven, non-blocking I/O model that supposedly makes it easy to build scalable network application. In this talk you will learn about the consequences of combining the event-driven programming model with a prototype-based, weakly typed, dynamic language. We will share our perspective as a server-side Java developer who wasn’t entirely happy about JavaScript in the browser, let alone on the server. You will learn how to use Node.js effectively in modern, polyglot applications.

Watch the video: http://www.youtube.com/watch?v=CN0jTnSROsk&feature=youtu.be

Statistics

Views

Total Views
10,133
Views on SlideShare
3,856
Embed Views
6,277

Actions

Likes
16
Downloads
0
Comments
0

16 Embeds 6,277

http://plainoldobjects.com 3736
http://jaxenter.com 1002
http://marakana.com 639
https://thenewcircle.com 473
http://www.scoop.it 313
http://www.pykode.com 71
http://localhost 28
http://127.0.0.1 7
http://www.newsblur.com 1
http://pykode.tumblr.com 1
http://feedly.com 1
http://www.tumblr.com 1
http://crcl.to 1
http://translate.googleusercontent.com 1
http://mrkn.co 1
http://news.google.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

NodeJS: the good parts? A skeptic’s view (jax jax2013) NodeJS: the good parts? A skeptic’s view (jax jax2013) Presentation Transcript

  • @crichardsonNodeJS: the good parts?A skeptic’s viewChris RichardsonAuthor of POJOs in ActionFounder of the original CloudFoundry.com@crichardsonchris.richardson@springsource.comhttp://plainoldobjects.com
  • @crichardsonPresentation goalHow a curmudgeonlyserver-side Java developerdiscovered an appreciationfor NodeJS and JavaScript
  • @crichardsonAbout Chris
  • @crichardson(About Chris)
  • @crichardsonAbout Chris()
  • @crichardsonAbout Chris
  • @crichardsonAbout Chrishttp://www.theregister.co.uk/2009/08/19/springsource_cloud_foundry/
  • @crichardsonvmc push About-ChrisDeveloper AdvocateSignup at http://cloudfoundry.com
  • @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  • @crichardsonWhat’s NodeJS?Designed for DIRTy apps
  • @crichardsonSmall but growing rapidlyBusy!
  • @crichardsonNodeJS Hello Worldapp.js$ node app.js$ curl http://localhost:1337http://nodejs.org/Load a modulerequest handler
  • @crichardsonNodeJSJavaScriptReactorpatternModules
  • @crichardsonNodeJSJavaScriptReactorpatternModules
  • @crichardsonDynamic, weakly-typedDynamic:Types are associated with values - not variablesDefine new program elements at runtimeWeakly typed:Leave out arguments to methodsAccess non-existent object propertiesWeird implicit conversions: 99 == “99”!truthy and falsy valuesComprehensive tests are essential
  • @crichardsonJavaScript is object-oriented> var fred = {name: “Fred”, gender: “Male”};undefined> fred.name“Fred”> console.log("reading age=" + fred.age);reading age=undefinedundefined> fred.age = 99;99> fred{ name: Fred,gender: Male,age: 99 }> delete fred.agetrue> fred{ name: Fred, gender: Male }Unordered key-value pairsKeys = propertiesAdd propertyDelete property
  • @crichardsonJavaScript “classes”function Person(name) {this.name = name;}Person.prototype.sayHello = function () { console.log("Hello " + this.name); };var chris = new Person("Chris");chris.sayHello();Looks like aconstructor?!?What’s that?!?!This Java-like syntax is a messbecause JavaScript isn’t classbasedLooks familiar
  • @crichardsonoverridesJavaScript is a prototypallanguage__proto__a 99__proto__a 100b 200inheritedPrototypeXY
  • @crichardsonPrototypal code$ node> var person = {};undefined> person.sayHello = function () { console.log("Hello " + this.name); };[Function]> var chris = Object.create(person, {name: {value: "Chris"}});undefined> var sarah = Object.create(person, {name: {value: "Sarah"}});undefined> chris.sayHello();Hello Chrisundefined> sarah.sayHello();Hello Sarahundefined> chris.sayHello = function () { console.log("Hello mate: " + this.name); };[Function]> chris.sayHello();Hello mate: ChrisundefinedNot definedhereprototype properties
  • @crichardsonJavaScript is Functionalfunction makeGenerator(nextFunction) {var value = 0;return function() {var current = value;value = nextFunction(value);return current;};}var inc = makeGenerator(function (x) {return x + 1; });> inc()0> inc()1Pass function as anargumentReturn a functionclosure
  • @crichardsonPartial function application> var join = require("path").join;undefined> join("/x", "y")/x/y> var withinx = join.bind(undefined, "/x");undefined> withinx("y");/x/y>partially apply join
  • @crichardsonCreated in a hurry with thegoal of looking like JavaThe ‘Java...’ name creates expectations that it can’t satisfyFake classes: Hides prototypes BUT still seems weirdglobal namespacescope of vars is confusingMissing return statement = confusion‘function’ is really verbose‘this’ is dynamically scoped
  • @crichardsonJavaScript is the only way toget things done in thebrowser
  • @crichardsonStockholm syndrome“Stockholm syndrome ... is a psychologicalphenomenon in which hostages ... havepositive feelings toward their captors,sometimes to the point of defending them...”http://en.wikipedia.org/wiki/Stockholm_syndrome
  • @crichardsonMartin Fowler once said:"...Im one of those who despairs that alanguage with such deep flaws plays such animportant role in computation. Still theconsequence of this is that we must takejavascript seriously as a first-class languageand concentrate on how to limit the damageits flaws cause. ...."http://martinfowler.com/bliki/gotoAarhus2012.html
  • @crichardsonUse just the good parts
  • @crichardsonUse a better language thatcompiles to JavaScriptTypeScriptTyped parameters and fieldsClasses and interfaces (dynamic structural typing)DartClass-based OOOptional static typingBidirectional binding with DOM elements
  • @crichardsonCoffeeScript Hello Worldhttp = require(http)class HttpRequestHandlerconstructor: (@message) ->handle: (req, res) =>res.writeHead(200, {Content-Type: text/plain})res.end(@message + n)handler = new HttpRequestHandler "Hi There from CoffeeScript"server = http.createServer(handler.handle)server.listen(1338, 127.0.0.1)console.log(Server running at http://127.0.0.1:1338/)Classes :-)Bound method
  • @crichardsonNodeJSJavaScriptReactorpatternModules
  • @crichardsonAbout the Reactor patternDefined by Doug Schmidt in 1995Pattern for writing scalable serversAlternative to thread-per-connection modelSingle threaded event loop dispatches events onhandles (e.g. sockets, file descriptors) to event handlers
  • @crichardsonReactor pattern structureEvent Handlerhandle_event(type)get_handle()Initiation Dispatcherhandle_events()register_handler(h)select(handlers)for each h in handlersh.handle_event(type)end loophandleSynchronous EventDemultiplexerselect()ownsnotifiesuseshandlers
  • @crichardsonBenefits:Separation of concerns - event handlers separatedfrom low-level mechanismMore efficient - no thread context switchingSimplified concurrency - single threaded
  • @crichardsonDrawbacks:Non-pre-emptive - handlers can’t block/take a longtimeDifficult to understand and debug - inverted flow ofcontrol
  • @crichardsonNodeJS event loop implementsthe Reactor pattern
  • @crichardsonApplication codeEvent-driven architectureNodeJS event loopBasic networking/file-system/etc.HTTP DB driver ...EventlistenerCallbackfunctionOnetimeeventsRecurringevents
  • @crichardsonGetting notified: Callbackexamplevar fs = require("fs");function statFile(path) {fs.stat(path, function (err, stat) {if (err) {console.log("Stat failed: " + path, err);throw err;}console.log("stat result=" + path, stat);});};By convention: firstparam is error objectBy convention: Lastarg is a callbackCallbacks aregood for onetimenotifications
  • @crichardsonGetting notified: eventlistenersEventEmitter class - inherit or useListener registration methods:on(eventName, listener)once(eventName, listener)Emitting eventsemit(eventName, args...)‘error’ event = special case: no listener print stack trace andexit!Good forrecurringevents
  • @crichardsonEvent listener examplevar fs = require("fs");var readStream = fs.createReadStream("events.js", {encoding: "utf-8"});// ReadStream << ReadableStream << EventEmitterreadStream.on(open, function (fd) {console.log("opened with fd=", fd);});// Node v0.10 has readable instead: this is deprecatedreadStream.on(data, function (data) {console.log("data=", data);});Register listenerRegister listener
  • @crichardsonCallback hellfunction times2(x, callback) {setTimeout(function () {callback(x * 2)}, 500);}function plus3(x, callback) {setTimeout(function (){callback(x + 3)}, 500);}function displayResult(z) {console.log("The result is=", z);}function plus3AndThenTimes2(x, callback){plus3(x, function (y) {times2(y, callback)})}plus3AndThenTimes2(10, displayResults);function sum(a, b, callback) {setTimeout(function () {callback(a + b);}, 500);}function plus3PlusTimes2(x, callback) {var p3, t2;function perhapsDone() {if (p3 & t2)sum(p3, t2, callback);};plus3(x, function (y) {p3 = y;perhapsDone();});times2(x, function (y) {t2 = y;perhapsDone();});}plus3PlusTimes2(10, displayResult);times2(plus3(x)) times2(x) + plus3(x)
  • @crichardsonLong running computationsLong running computation blocks event loop forother requestsNeed to run outside of main event loopOptions:Community: web workers threadsBuilt-in: NodeJS child processes
  • @crichardsonUsing child processesvar child = require(child_process).fork(child.js);function sayHelloToChild() {child.send({hello: "child"});}setTimeout(sayHelloToChild, 1000);child.on(message, function(m) {console.log(parent received:, m);});function kill() {child.kill();}setTimeout(kill, 2000);process.on(message, function (m) {console.log("child received message=", m);process.send({ihateyou: "you ruined my life"})});parent.jschild.jsCreate child processSend message to child
  • @crichardsonNodeJSJavaScriptReactorpatternModules
  • Core built-in modulesBasic networkingHTTP(S)FilesystemEventsTimers...
  • @crichardsonThousands of communitydeveloped moduleshttps://npmjs.org/web frameworks, SQL/NoSQLdatabase, drivers, messaging, utilities...
  • @crichardsonWhat’s a module?One or more JavaScript filesOptional native code:Compiled during installationJavaScript != systems programming languagePackage.json - metadata including dependenciesexports.sayHello = function () {console.log(“Hello”);}foo.js
  • @crichardsonEasy to install$ npm install package-name
  • @crichardsonEasy to usevar http = require(“http”)var server = http.createServer...Core module ORPath to file ORmodule in node_modulesModule’s exports
  • @crichardsonDeveloping with NodeJSmodulesCore modulesCommunity modulesYour modulesApplication code
  • @crichardsonLots of modules BUT...Variable qualityMultiple incomplete MySQL drivers, e.g. withoutconnection pooling!Often abandoned...
  • @crichardsonTo summarizeNodeJSJavaScriptReactor patternModulesFlawed andmisunderstoodScalable yetcostly andoftenunnecessaryRich butvariable quality
  • @crichardsonAlternative technologiesAtmosphere - portable event deliveryNetty - asynchronous I/OVert.xSpringSource’s Reactor project...
  • @crichardsonSo why care aboutNodeJS?Easy to write scalable network servicesEasy to push events to the browserEasy to get (small) stuff doneIt has a role to play in modernapplication architecture
  • @crichardsonEvolving from a monolithicarchitecture....WARShippingServiceAccountingServiceInventoryServiceStoreFrontUI
  • @crichardson... to a micro-service architectureStore front web applicationshipping web applicationinventory web applicationAccountingServiceStoreFrontUIaccounting web applicationShippingServiceInventoryService
  • @crichardsonBrowserWARStoreFrontUIModelView ControllerPresentation layer evolution....HTML / HTTP+JavaScript
  • @crichardsonBrowser Web applicationRESTfulEndpointsModelView Controller...Presentation layer evolutionJSON-RESTHTML 5 -JavaScriptNo elaborate, server-side web frameworkrequiredEventpublisherEventsStaticcontent
  • @crichardsonNodeJS as an API gatewayBrowserService 1Service 2MessageBusHTML 5/JavaScriptSocket.ioclientEventsRESTful WSServerapplicationSocket.ioserverNode JSService 3RESTfulWS
  • @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  • @crichardsonServing static content
  • @crichardsonUsing low-level APIsvar http = require(http), path = require(path), mime = require(mime), fs = require("fs");var server = http.createServer(function (req, res) {var filePath;if (req.url == /) {filePath = public/index.html;} else {filePath = public + req.url;}fs.exists(filePath, function (exists) {if (exists) {res.writeHead(200, {"content-type": mime.lookup(path.basename(filePath)) });fs.createReadStream(filePath).pipe(res);} else {res.writeHead(404, {Content-Type: text/plain});res.write(Error 404: resource not found.);res.end();}});});server.listen(1337, 127.0.0.1);console.log(Server running at http://127.0.0.1:1337/);Uses fileextensionCool!
  • @crichardsonUsing the express webframeworkvar express = require(express), http = require(http), app = express(), server = http.createServer(app);app.configure(function(){app.use(express.static(__dirname + /public));});server.listen(8081);
  • @crichardsonRESTful web services
  • @crichardsonImplementing RESTful WSwith Expressvar express = require(express),http = require(http),path = require(path);var app = express();var server = http.createServer(app);app.get(/portfolio/:userId, function (req, res) {var portfolio = retrievePortfolio(req.params.userId);res.json(portfolio);});Easy URLrouting anddestructuring
  • @crichardsonProxying to backend serverexpress = require(express)request = require(request)app = express.createServer()proxyToBackend = (baseUrl) ->(req, res) ->callback = (error, response, body) -> console.log("error=", error)originRequest = request(baseUrl + req.url, callback)req.pipe(originRequest)originRequest.pipe(res)app.get(/restaurant/*, proxyToBackend(http://available-restaurant....com))app.post(/orders, proxyToBackend(http://restaurant-management...com))app.get(/orders, proxyToBackend(http://restaurant-management...com))Returns a request handlerthat proxies to baseUrl
  • @crichardsonDelivering events to thebrowser
  • @crichardsonNodeJS clock exampleIncrements everysecond
  • @crichardsonSocket.io - Server sidevar express = require(express), http = require(http), app = express(), server = http.createServer(app), io = require(socket.io).listen(server);app.configure(function(){app.use(express.static(__dirname + /public));});server.listen(8081);io.sockets.on(connection, function (socket) { var counter = 0; function tick() { counter = counter + 1; socket.emit(tick, counter); }; setInterval(tick, 1000);});handle newconnectionSend tick event tobrowser every 1 secinitializes socket.io
  • @crichardsonSocket.io - client side using theknockout.js MVVM frameworkvar socket = io.connect(location.hostname);function ClockModel() {self.ticker = ko.observable(1);socket.on(tick, function (data) {self.ticker(data);});};ko.applyBindings(new ClockModel());<html><body>The event is <span data-bind="text: ticker"></span><script src="/socket.io/socket.io.js"></script><script src="/knockout-2.0.0.js"></script><script src="/clock.js"></script></body></html>clock.jsConnect tosocket.ioSubscribeto tick eventBind to modelUpdatemodel
  • @crichardsonDistributed NodeJS clockexampleBrowser NodeJS NodeJSRabbitMQProducerConsumersocket.iosocket.io
  • @crichardsonAMQP Publishervar amqp = require(amqp),amqpConnection = amqp.createConnection(...),tickTockExchange;function tick() {var message = { tick: Date.now() };tickTockExchange.publish("tickTock", message, {mandatory: true,contentType: "text/plain"});};amqpConnection.on(ready,function () {tickTockExchange =amqpConnection.exchange("tickTock",options = {passive: false, type: fanout});tickTockExchange.on(open, function () { setInterval(tick, 1000) });});Connect toAMQPEnsureexchangeexistsPublishmessageInitialize timer
  • @crichardsonAMQP socket.iovar express = require(express), http = require(http), amqp = require(‘amqp’)....;server.listen(8081);...var amqpCon = amqp.createConnection(...);io.sockets.on(connection, function (socket) {function amqpMessageHandler(message, headers, deliveryInfo) {var m = JSON.parse(message.data.toString());socket.emit(‘tick’, m);};amqpCon.queue(“”, {},function(queue) {queue.bind(“tickTock”, “”);queue.subscribe(amqpMessageHandler);});});Connect toAMQP queueSubscribe toAMQP queueRepublishas socket.ioeventhttps://github.com/cer/nodejs-clock
  • @crichardsonAgendaOverview of NodeJSBuilding a front-end server with NodeJSTaming tangled asynchronous code with promises
  • @crichardsonAsync code = callback hellScenarios:Sequential: A B CFork and join: A and B CCode quickly becomes very messy
  • @crichardsonSimplifying code withPromises (a.k.a. Futures)Functions return a promise - no callback parameterA promise represents an eventual outcomeUse a library of functions for transforming andcomposing promisesPromises/A+ specification - http://promises-aplus.github.io/promises-specwhen.js (part of cujo.js by SpringSource) is a popularimplementation
  • @crichardsonTaming callback hell 1function times2(x) {var deferred = when.defer();setTimeout(function () {deferred.resolve(x * 2)}, 500);return deferred.promise;}times2(plus3(x)) Create a deferredReturn a promiseEventually supply a valuefunction plus3AndThenTimes2(x) {return plus3(x).then(times2);}plus3AndThenTimes2(10).then(displayResult);Transform value inpromisefunction plus3(x) {var deferred = when.defer();setTimeout(function () {deferred.resolve(x + 3) }, 500);return deferred.promise;}Simpler, almostsynchronous-style code
  • @crichardsonTaming callback hell 2function sum(a, b) {var deferred = when.defer();setTimeout(function () {deferred.resolve(a + b);}, 500);return deferred.promise;}function plus3PlusTimes2(x) {var p3 = plus3(x),t2 = times2(x);return when.join(p3, t2).spread(sum);}plus3PlusTimes2(10).then(displayResult);times2(x) + plus3(x)Combine resultsof two promisesCall with arrayelements asarguments
  • @crichardsonCalling non-promise codevar deferred = when.defer();fs.stat(path, function (err, statInfo) {if (err)deferred.reject(err);elsedeferred.resolve(statInfo);}var promise = deferred.promise;var nodefn = require("when/node/function");var promise = nodefn.call(fs.stat, path)Hidesboilerplatecode
  • @crichardsonFilesystem scanner exampleRead contents of directoryUse stat to determine if directory or fileRecurse on directoriesMerge the results
  • @crichardsonRead contents of directoryfunction findFilesInDir(dir) {var directoryContents =nodefn.call(self.fs.readdir, dir);...}Returns promise containingan array of file names
  • @crichardsonCreate absolute pathsfunction findFilesInDir(dir) {var directoryContents = ...var toAbsolute = join.bind(undefined, dir)var absolutePaths = when.map(directoryContents, toAbsolute);...}Partially applyjoin()
  • @crichardsonUse stat to determine ifdirectory or filefunction findFilesInDir(dir) {var directoryContents = ...var absolutePaths = ...var statTasks = when.map(absolutePaths, makeStatTask);var statResults = parallel(statTasks);...}function makeStatTask(path) {return function () {function makeStatInfo(stats) {return {path: path, isdir: stats.isDirectory(),ctime: stats.ctime};}return nodefn.call(self.fs.stat, path).then(makeStatInfo);};}Execute stats inparallel
  • @crichardsonRecurse on directoriesfunction findFilesInDir(dir) {...var statResults = ...;var listOfListsOfFiles =when.map(statResults, processStatInfo);...}function processStatInfo(statInfo) {if (statInfo.isdir) {return findFilesInDir(statInfo.path);} else {return [statInfo.path];}}Map each statresult to a list offiles
  • @crichardsonFlatten array of arrays of filepathsfunction findFilesInDir(dir) {...var listOfListsOfFiles = ...;return when.reduce(listOfListsOfFiles,concatenateLists, []);}function concatenateLists(currentResult, value, index, total){return currentResult.concat(value);}
  • @crichardsonSummaryJavaScript is a very flawed languageThe asynchronous model is often unnecessary; veryconstraining; and adds complexityBUT despite those problemsToday, NodeJS is remarkably useful for building network-focussed components
  • @crichardsonQuestions?@crichardson chris.richardson@springsource.comhttp://plainoldobjects.com - code and slideswww.cloudfoundry.com