nodejs: the good parts? a skeptic’s view (oredev, oredev2013)

74
@crichardson NodeJS: the good parts? A skeptic’s view Chris Richardson Author of POJOs in Action Founder of the original CloudFoundry.com @crichardson [email protected] http://plainoldobjects.com

Upload: chris-richardson

Post on 06-May-2015

1.596 views

Category:

Technology


1 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS: the good parts? A skeptic’s view

Chris Richardson

Author of POJOs in ActionFounder of the original CloudFoundry.com

@[email protected] http://plainoldobjects.com

Page 2: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Presentation goal

How a grumpy, gray-haired server-side Java developer discovered an appreciation for NodeJS and JavaScript

Page 3: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

VIEWER DISCRETION IS ADVISED

WARNING!

Page 4: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

1982 1986

RPG 3 BCPLPascal

C

About Chris

1983

LispWorks

1980 1984 1985 1987 1988 19891981

Z806502Assembler

Basic

Page 5: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

C++

EJB

1992 199619931990 1994 1995 1997 1998 19991991

Page 6: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

CloudFoundry.com

2002 200620032000 2004 2005 2007 2008 20092001

Page 7: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

?About Chris

2012 201620132010 2014 2015 2017 2018 20192011

Page 8: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 9: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

What’s NodeJS?

Designed for DIRTy apps

Page 10: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Growing rapidly

Busy!

Page 11: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS Hello Worldapp.js

$ node app.js$ curl http://localhost:1337

http://nodejs.org/

Load a module

request handler

No complex configuration: simple!

Page 12: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 13: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 14: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Dynamic and weakly-typedDynamic:

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

Page 15: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

JavaScript 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 pairs

Keys = properties

Add property

Delete property

Page 16: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

overrides

JavaScript is a prototypal language

__proto__name “Chris”

__proto__sayHello function

... ...

inherited

Prototype

Person

Chris

“CER”nicknameobject specific

Page 17: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Prototypal code$ 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 Chrisundefined> sarah.sayHello();Hello Sarahundefined> chris.sayHello = function () { console.log("Hello mate: " + this.name); };[Function]> chris.sayHello();Hello mate: Chrisundefined

Not defined here

create using prototype properties

Page 18: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

JavaScript 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()1

Pass function as an argument

Return a function closure

Page 19: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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 confusingMissing return statement = confusion

‘function’ is really verbose

‘this’ is dynamically scoped

Unexpected implicit conversions: 99 == “99”!

truthy and falsy values52-bit ints

Page 20: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

Dynamic + weakly-typed (+ event-driven) code

+ misspelt property names

lots of time spent in the abyss

Essential: Use IDE integrated with JSLint/JSHint + tests

Page 21: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

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

Page 22: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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 ]>

scala> val numbers = 1..5scala> numbers filter { _ % 2 == 0} map { n => n * n }Vector(4, 16)

VersusPrelude> let numbers = [1,2,3,4,5]Prelude> map (\n -> n * n) (filter (\n -> mod n 2 == 0) numbers)[4,16]

Or

Page 23: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Verbose DSLsdescribe('SomeEntity', function () {

beforeEach(function () { ... some initialization ... });

it('should do something', function () { ... expect(someExpression).toBe(someValue); });});

class SomeScalaTest ...{

before { ... some initialization ... }

it should "do something" in { ... someExpression should be(someValue)}

Versus

Jasmine

Scalatest

Page 24: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

JavaScript is the language of the web

“You have to use the programming language you have, not the one that you

might want”

Page 25: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

It works but the result is lost opportunities

and impeded progress

Page 26: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 27: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Use just the good parts

http://www.crockford.com/

Douglas Crockford

Page 28: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 29: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

CoffeeScript Hello Worldhttp = require('http')

class HttpRequestHandler constructor: (@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

Concise

Page 30: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

No escaping JavaScript

Page 31: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 32: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 33: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Reactor pattern structure

Event Handlerhandle_event(type)get_handle()

Initiation Dispatcherhandle_events() register_handler(h)

select(handlers)for each h in handlers h.handle_event(type)end loop

handleSynchronous Event

Demultiplexerselect()

owns

notifies

uses

handlers

Applicationregister_handler(h1)register_handler(h2)handle_events()

Page 34: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 35: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

DrawbacksNon-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?

Page 36: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Application code

NodeJS app = layers of event handlers

NodeJS event loop

Basic networking/file-system/etc.

HTTP DB driver ...

Event listener

Callback function

One time events:async

operation completion

Recurring events from Event

Emitters

Page 37: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Async code = callback hell

Scenarios:

Sequential: A ⇒ B ⇒ C

Scatter/Gather: A and B ⇒ C

Code quickly becomes very messy

Page 38: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Messy callback codegetProductDetails = (productId, callback) -> productId = req.params.productId result = {productId: productId} makeCallbackFor = (key) -> (error, x) -> if error

callback(error) else result[key] = x if (result.productInfo and result.recommendations and result.reviews) callback(undefined, result)

getProductInfo(productId, makeCallbackFor('productInfo')) getRecommendations(productId, makeCallbackFor('recommendations')) getReviews(makeCallbackFor('reviews'))

The result of getProductDetails

Gather

Scatter

Update result

Propagate error

Page 39: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 40: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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)]

all(responses).spread(makeProductDetails)all(responses) spread(makeProductDetails)

responses = [getProductInfo(productId), getRecommendations(productId),

getReviews(productId)]

Page 41: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Not bad but lacks Scala’s syntactic sugar

class ProductDetailsService .... {

def getProductDetails(productId: Long) = {

for (((productInfo, recommendations), reviews) <- getProductInfo(productId) zip getRecommendations(productId) zip getReviews(productId)) yield ProductDetails(productInfo, recommendations, reviews) }

}

getProductInfo(productId) zip getRecommendations(productId) zip getReviews(productId)

yield ProductDetails(productInfo, recommendations, reviews)

for (((productInfo, recommendations), reviews) <-

Page 42: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 43: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Using 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.js

child.js

Create child process

Send message to child

Page 44: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 45: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS

JavaScript

Reactor pattern

Modules

Page 46: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

Core built-in modules

Basic networking

HTTP(S)

Filesystem

Events

Timers

...

Page 47: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Thousands of community developed modules

https://npmjs.org/

web frameworks, SQL/NoSQL database drivers, messaging, utilities...

Page 48: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

What’s a module?

One or more JavaScript files

Optional native code:

Compiled during installation

JavaScript != systems programming language

Package.json - metadata including dependencies

exports.sayHello = function () { console.log(“Hello”);}

foo.js

Page 49: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Easy to install

$ npm install package-name --save

Page 50: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Easy to use

var http = require(“http”)var server = http.createServer...

Core module ORPath to file ORmodule in node_modules

Module’s exports

Page 51: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Developing with NodeJS modules

Core modules

Community modules

Your modules

Application code

Page 52: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

There is a module for that...

Modules + glue code =

rapid/easy application development

AWESOME!...

Page 53: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

...

Page 54: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

To summarize

NodeJS

JavaScript

Reactor patternModules

Flawed and misunderstood

Scalable yet costly and

often unnecessary

Rich but variable quality

Page 55: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

How will future history view NodeJS?

C++EJB

?

Page 56: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 57: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@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

Page 58: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Evolving from a monolithic architecture....

WAR

ReviewService

Product InfoService

RecommendationService

StoreFrontUI

OrderService

Page 59: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

... to a micro-service architecture

Store front application

reviews application

recommendations application

product info application

ReviewService

Product InfoService

RecommendationService

StoreFrontUI

OrderService

orders application

Page 60: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Browser

WAR

StoreFrontUI

Model

View Controller

Presentation layer evolution....

HTML / HTTP

+ JavaScript

Page 61: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Browser Web application

RESTfulEndpointsModel

View Controller

...Presentation layer evolution

JSON-REST

HTML 5/JavaScriptIOS/Android clients

Event publisher

Events

Static content

Page 62: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Directly connecting the front-end to the backend

Model

View Controller Product Infoservice

RecommendationService

Reviewservice

REST

REST

AMQP

Model

View Controller

Browser/Native App

Traditional web application

Chatty API

Web unfriendly protocols

Page 63: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS as an API gatewayBrowser

Model

View Controller

HTML 5 - JavaScript

Product Infoservice

RecommendationService

Reviewservice

REST

REST

AMQP

APIGateway

Native App

Model

View Controller

Single entry point

Optimized Client specific APIs

Protocol translation

RESTproxy

Event publishing

NodeJS

Page 64: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Serving static content with the Express web framework

var express = require('express') , http = require('http') , app = express() , server = http.createServer(app) ;

app.configure(function(){ ... app.use(express.static(__dirname + '/public'));});

server.listen(8081);

From public sub directory

Page 65: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

RESTful web services

Page 66: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Proxying 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('/productinfo/*', proxyToBackend('http://productinfo....'))

app.get('/recommendations/*', proxyToBackend(''http://recommendations...'))

app.get('/reviews/*', proxyToBackend('http://reviews...'))

Returns a request handler that proxies to baseUrl

Page 67: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Implementing coarse-grained mobile API

var express = require('express'), ...;

app.get('/productdetails/:productId', function (req, res) { getProductDetails(req.params. productId).then( function (productDetails) { res.json(productDetails); }});

Page 68: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Delivering events to the browser

Page 69: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Socket.io server-sidevar 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(“myExchange”, “”); queue.subscribe(amqpMessageHandler); });});

Handle socket.io

connection

Subscribe to AMQP queue

Republish as socket.io

event

https://github.com/cer/nodejs-clock

Page 70: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Socket.io - client side

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());

<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.js

Connect to socket.io

Subscribe to tick event

Bind to model

Update model

Page 71: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS is also great for writing backend micro-services

“Network elements”

Simply ‘route, filter and transform packets’

Have minimal business logic

Page 72: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

NodeJS-powered home security

Upload2S3 UploadQueueProcessor

SQS Queue DynamoDBS3

FTP ServerLog file

FTP ServerUpload directory

Page 73: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

SummaryJavaScript 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 network-focussed components

Page 74: NodeJS: the good parts? A skeptic’s view (oredev, oredev2013)

@crichardson

Questions?

@crichardson [email protected]

http://plainoldobjects.com