• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
NodeJS: the good parts? A skeptic’s view (jmaghreb, jmaghreb2013)
 

NodeJS: the good parts? A skeptic’s view (jmaghreb, jmaghreb2013)

on

  • 849 views

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

JavaScript used to be confined to the browser. But these days, it becoming increasingly popular in server-side applications in the form of NodeJS. NodeJS 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 NodeJS effectively in modern, polyglot applications.

Statistics

Views

Total Views
849
Views on SlideShare
845
Embed Views
4

Actions

Likes
0
Downloads
16
Comments
0

1 Embed 4

http://www.linkedin.com 4

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 (jmaghreb, jmaghreb2013) NodeJS: the good parts? A skeptic’s view (jmaghreb, jmaghreb2013) Presentation Transcript

    • NodeJS: the good parts? A skeptic’s view Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson chris@chrisrichardson.net http://plainoldobjects.com @crichardson
    • Presentation goal How a grumpy, gray-haired server-side Java developer discovered an appreciation for NodeJS and JavaScript @crichardson
    • ! WARNING VIEWER DISCRETION IS ADVISED @crichardson
    • About Chris LispWorks 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 Z80 RPG 3 BCPL Pascal 6502 C Assembler Basic @crichardson
    • EJB 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 C++ @crichardson
    • CloudFoundry.com 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 @crichardson
    • About Chris ? 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 @crichardson
    • Agenda Overview of NodeJS JavaScript: Warts and all The Reactor pattern: an event-driven architecture NodeJS: There is a module for that Building a front-end server with NodeJS @crichardson
    • What’s NodeJS? Designed for DIRTy apps @crichardson
    • Growing rapidly Busy! @crichardson
    • NodeJS Hello World app.js Load a module request handler $ node app.js $ curl http://localhost:1337 http://nodejs.org/ No complex configuration: simple! @crichardson
    • JavaScript NodeJS Modules Reactor pattern @crichardson
    • JavaScript NodeJS Modules Reactor pattern @crichardson
    • Dynamic and weakly-typed Dynamic: Types are associated with values - not variables Define new program elements at runtime Weakly typed: Leave out arguments to methods Read non-existent object properties Add new properties by simply setting them @crichardson
    • JavaScript is object-oriented > var fred = {name: “Fred”, gender: “Male”}; undefined > fred.name “Fred” > console.log("reading age=" + fred.age); reading age=undefined undefined > fred.age = 99; 99 > fred { name: 'Fred', gender: 'Male', age: 99 } > delete fred.age true > fred { name: 'Fred', gender: 'Male' } Unordered key-value pairs Keys = properties Add property Delete property @crichardson
    • JavaScript is a prototypal language Person __proto__ sayHello ... Prototype function ... inherited Chris __proto__ name nickname “Chris” “CER” overrides object specific @crichardson
    • Prototypal code Not defined here $ node > var 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 Chris create using undefined properties > sarah.sayHello(); prototype Hello Sarah undefined > chris.sayHello = function () { console.log("Hello mate: " + this.name); }; [Function] > chris.sayHello(); Hello mate: Chris undefined @crichardson
    • JavaScript is Functional function makeGenerator(nextFunction) { Return a function closure var value = 0; return function() { var current = value; value = nextFunction(value); return current; }; Pass function as an argument } var inc = makeGenerator(function (x) {return x + 1; }); > inc() 0 > inc() 1 @crichardson
    • But JavaScript was created in a hurry The ‘Java...’ name creates expectations that it can’t satisfy Fake classes: Hides prototypes BUT still seems weird global namespace scope of vars is confusing Missing return statement = confusion ‘function’ is really verbose ‘this’ is dynamically scoped Unexpected implicit conversions: 99 == “99”! truthy and falsy values 52-bit ints @crichardson
    • Dynamic + weakly-typed (+ event-driven) code + misspelt property names lots of time spent in the abyss Essential: Use IDE integrated with JSLint/JSHint + tests
    • Prototypal languages have benefits BUT Developers really like classes JavaScript prototypes lack the powerful features from the Self language e.g. Multiple (and dynamic) inheritance http://www.cs.ucsb.edu/~urs/oocsb/self/papers/papers.html @crichardson
    • Verbose function syntax > var numbers = [1,2,3,4,5] > numbers.filter(function (n) { return n % 2 == 0; } ).map(function (n) { return n * n; }) [ 4, 16 ] > Versus Prelude> let numbers = [1,2,3,4,5] Prelude> map (n -> n * n) (filter (n -> mod n 2 == 0) numbers) [4,16] Or scala> val numbers = 1..5 scala> numbers filter { _ % 2 == 0} map { n => n * n } Vector(4, 16) @crichardson
    • Verbose DSLs describe('SomeEntity', function () { beforeEach(function () { ... some initialization ... }); Jasmine it('should do something', function () { ... expect(someExpression).toBe(someValue); }); }); Versus class SomeScalaTest ...{ Scalatest before { ... some initialization ... } it should "do something" in { ... someExpression should be(someValue) } @crichardson
    • JavaScript is the language of the web “You have to use the programming language you have, not the one that you might want” @crichardson
    • It works but the result is lost opportunities and impeded progress @crichardson
    • But if you think that this isn’t a problem then perhaps .... “Stockholm syndrome ... is a psychological phenomenon in which hostages ... have positive feelings toward their captors, sometimes to the point of defending them...” http://en.wikipedia.org/wiki/Stockholm_syndrome @crichardson
    • Martin Fowler once said: "...I'm one of those who despairs that a language with such deep flaws plays such an important role in computation. Still the consequence of this is that we must take javascript seriously as a first-class language and concentrate on how to limit the damage its flaws cause. ...." http://martinfowler.com/bliki/gotoAarhus2012.html @crichardson
    • Use just the good parts Douglas Crockford http://www.crockford.com/ @crichardson
    • Use a language that compiles to JavaScript TypeScript Classes and interfaces (dynamic structural typing) Typed parameters and fields Dart Class-based OO Optional static typing Bidirectional binding with DOM elements Less backwards compatibility with JavaScript Also has it’s own VM @crichardson
    • CoffeeScript Hello World Classes :-) http = require('http') Concise class HttpRequestHandler constructor: (@message) -> Bound method 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/') @crichardson
    • No escaping JavaScript @crichardson
    • JavaScript NodeJS Modules Reactor pattern @crichardson
    • About the Reactor pattern Defined by Doug Schmidt in 1995 Pattern for writing scalable servers Alternative to thread-per-connection model Single threaded event loop dispatches events on handles (e.g. sockets, file descriptors) to event handlers @crichardson
    • Reactor pattern structure Application register_handler(h1) register_handler(h2) handle_events() Initiation Dispatcher handle_events() handlers register_handler(h) uses select(handlers) for each h in handlers h.handle_event(type) end loop Event Handler handle_event(type) get_handle() owns Synchronous Event Demultiplexer select() notifies handle @crichardson
    • Benefits Separation of concerns - event handlers separated from low-level mechanism More efficient - no thread context switching Simplified concurrency - single threaded = no possibility of concurrent access to shared state @crichardson
    • Drawbacks Non-pre-emptive - handlers must not take a long time Difficult to understand and debug: Inverted flow of control Can’t single step through code easily Limited stack traces No stack-based context, e.g. thread locals, exception handlers How to enforce try {} finally {} behavior? @crichardson
    • NodeJS app = layers of event handlers Recurring events from Event Emitters Application code Event listener HTTP Callback function DB driver ... One time events: async operation completion Basic networking/file-system/etc. NodeJS event loop @crichardson
    • Async code = callback hell Scenarios: Sequential: A B C Scatter/Gather: A and B C Code quickly becomes very messy @crichardson
    • Messy callback code The result of getProductDetails getProductDetails = (productId, callback) -> productId = req.params.productId result = {productId: productId} Propagate error makeCallbackFor = (key) -> (error, x) -> if error callback(error) else result[key] = x if (result.productInfo and result.recommendations and result.reviews) callback(undefined, result) Update result Gather getProductInfo(productId, makeCallbackFor('productInfo')) getRecommendations(productId, makeCallbackFor('recommendations')) getReviews(makeCallbackFor('reviews')) Scatter @crichardson
    • Simplifying code with Promises (a.k.a. Futures) Functions return a promise - no callback parameter A promise represents an eventual outcome Use a library of functions for transforming and composing promises Promises/A+ specification - http://promises-aplus.github.io/promises-spec when.js (part of cujo.js by SpringSource) is a popular implementation Crockford’s RQ library is another option @crichardson
    • Simpler promise-based code class ProductDetailsService getProductDetails: (productId) -> makeProductDetails = (productInfo, recommendations, reviews) -> productId: productId productDetails: productInfo.entity recommendations: recommendations.entity reviews: reviews.entity responses = [getProductInfo(productId), getRecommendations(productId), getReviews(productId)] getReviews(productId)] all(responses).spread(makeProductDetails) all(responses) spread(makeProductDetails) @crichardson
    • Not bad but lacks Scala’s syntactic sugar class ProductDetailsService .... { def getProductDetails(productId: Long) = { for (((productInfo, recommendations), reviews) <getProductInfo(productId) zip getProductInfo(productId) zip getRecommendations(productId) zip getRecommendations(productId) zip getReviews(productId) getReviews(productId)) yield ProductDetails(productInfo, recommendations, reviews) } } @crichardson
    • Long running computations Long running computation blocks event loop for other requests Need to run outside of main event loop Options: Community: web workers threads Built-in: NodeJS child processes @crichardson
    • Using child processes parent.js var child = require('child_process').fork('child.js'); function sayHelloToChild() { child.send({hello: "child"}); } Create child process Send message to child setTimeout(sayHelloToChild, 1000); child.on('message', function(m) { console.log('parent received:', m); }); function kill() { child.kill(); } setTimeout(kill, 2000); child.js process.on('message', function (m) { console.log("child received message=", m); process.send({ihateyou: "you ruined my life"}) }); @crichardson
    • Modern multi-core machines vs. single-threaded runtime Many components of many applications Don’t need the scalability of the Reactor pattern Request-level thread-based parallelism works fine There are other concurrency options Actors, Software transactional memory, ... Go goroutines, Erlang processes, ... Imposing a single-threaded complexity tax on the entire application is questionable @crichardson
    • JavaScript NodeJS Modules Reactor pattern @crichardson
    • Core built-in modules Basic networking HTTP(S) Filesystem Events Timers ...
    • Thousands of community developed modules https://npmjs.org/ web frameworks, SQL/NoSQL database drivers, messaging, utilities... @crichardson
    • What’s a module? foo.js One or more JavaScript files exports.sayHello = function () { console.log(“Hello”); } Optional native code: Compiled during installation JavaScript != systems programming language Package.json - metadata including dependencies @crichardson
    • Easy to install $ npm install package-name --save @crichardson
    • Easy to use Core module OR Path to file OR module in node_modules Module’s exports var http = require(“http”) var server = http.createServer... @crichardson
    • Developing with NodeJS modules Application code Your modules Community modules Core modules @crichardson
    • There is a module for that... Modules + glue code = rapid/easy application development AWESOME!... @crichardson
    • ... BUT Variable quality Multiple incomplete/competing modules, e.g. MySQL drivers without connection pooling! Often abandoned No notion of a Maven-style local repository/cache = repeated downloads ... @crichardson
    • To summarize Flawed and misunderstood JavaScript Rich but variable quality NodeJS Modules Scalable yet costly and often unnecessary Reactor pattern @crichardson
    • How will future history view NodeJS? C++ ? EJB @crichardson
    • Agenda Overview of NodeJS JavaScript: Warts and all The Reactor pattern: an event-driven architecture NodeJS: There is a module for that Building a front-end server with NodeJS @crichardson
    • So why care about NodeJS? Easy to write scalable network services Easy to push events to the browser Easy to get (small) stuff done It has a role to play in modern application architecture @crichardson
    • Evolving from a monolithic architecture.... WAR StoreFrontUI Product Info Service Recommendation Service Review Service Order Service @crichardson
    • ... to a micro-service architecture product info application Product Info Service recommendations application Store front application StoreFrontUI Recommendation Service reviews application Review Service orders application Order Service @crichardson
    • Presentation layer evolution.... WAR StoreFrontUI HTML / HTTP View Controller Browser + JavaScript Model @crichardson
    • ...Presentation layer evolution Browser View Web application Static content Controller JSON-REST Model HTML 5/JavaScript IOS/Android clients Events RESTful Endpoints Event publisher @crichardson
    • Directly connecting the front-end to the backend Chatty API View REST Product Info service REST Recommendation Service AMQP Controller Review service Model Traditional web application View Controller Model Browser/Native App Web unfriendly protocols @crichardson
    • NodeJS as an API gateway Single entry point Browser View Controller NodeJS Model HTML 5 - JavaScript Product Info service REST Recommendation Service AMQP Review service REST proxy Native App View API Gateway REST Controller Model Event publishing Optimized Client specific APIs Protocol translation @crichardson
    • Serving static content with the Express web framework var express = require('express') , http = require('http') , app = express() , server = http.createServer(app) ; From public sub directory app.configure(function(){ ... app.use(express.static(__dirname + '/public')); }); server.listen(8081); @crichardson
    • RESTful web services @crichardson
    • Proxying to backend server express = require('express') request = require('request') Returns a request handler that proxies to baseUrl 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('/productinfo/*', proxyToBackend('http://productinfo....')) app.get('/recommendations/*', proxyToBackend(''http://recommendations...')) app.get('/reviews/*', proxyToBackend('http://reviews...')) @crichardson
    • Implementing coarsegrained mobile API var express = require('express'), ...; app.get('/productdetails/:productId', function (req, res) { getProductDetails(req.params. productId).then( function (productDetails) { res.json(productDetails); } }); @crichardson
    • Delivering events to the browser @crichardson
    • Socket.io server-side var express = require('express') , http = require('http') , amqp = require(‘amqp’) ....; server.listen(8081); ... var amqpCon = amqp.createConnection(...); Handle socket.io connection io.sockets.on('connection', function (socket) { function amqpMessageHandler(message, headers, deliveryInfo) { Republish var m = JSON.parse(message.data.toString()); socket.emit(‘tick’, m); as socket.io }; event amqpCon.queue(“”, {}, function(queue) { queue.bind(“myExchange”, “”); queue.subscribe(amqpMessageHandler); Subscribe to }); }); AMQP queue https://github.com/cer/nodejs-clock @crichardson
    • Socket.io - client side <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> Bind to model Connect to socket.io clock.js var socket = io.connect(location.hostname); function ClockModel() { self.ticker = ko.observable(1); socket.on('tick', function (data) { self.ticker(data); }); }; ko.applyBindings(new ClockModel()); Subscribe to tick event Update model @crichardson
    • NodeJS is also great for writing backend micro-services “Network elements” Simply ‘route, filter and transform packets’ Have minimal business logic @crichardson
    • NodeJS-powered home security FTP Server Log file FTP Server Upload directory Upload2S3 S3 SQS Queue UploadQueue Processor DynamoDB @crichardson
    • Summary JavaScript is a very flawed language The asynchronous model is often unnecessary; very constraining; and adds complexity BUT despite those problems Today, NodeJS is remarkably useful for building networkfocussed components @crichardson
    • @crichardson chris@chrisrichardson.net Questions? http://plainoldobjects.com @crichardson