52
How NOT to write in Node.js Piotr Pelczar [email protected] PHPers, 6 Feb 2014

How NOT to write in Node.js

Embed Size (px)

DESCRIPTION

Node.js facts and myths, revealed architecture and scaling eventapproach.

Citation preview

Page 1: How NOT to write in Node.js

How NOT to write in Node.js

Piotr [email protected]

PHPers, 6 Feb 2014

Page 2: How NOT to write in Node.js

About me

Piotr [email protected]

Page 3: How NOT to write in Node.js

About me

getfokus.com useselly.com

Page 4: How NOT to write in Node.js

How software lives in hardware?

•Operating systems are process based• Each process has assigned processor,

registers, memory

http://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html

Page 5: How NOT to write in Node.js

How software lives in hardware?

•Process paralelism using threads (thread pools)• Switching processor over

processes/threads causes context switching

http://www.cs.uic.edu/~jbell/CourseNotes/OperatingSystems/4_Threads.html

Page 6: How NOT to write in Node.js

How software lives in hardware?

1. Avoid context switching = wasting time

Page 7: How NOT to write in Node.js

How software lives in hardware?

In trivial, sequential approach

• Each operation is executed sequentially:

O(t) > O(t+1)

• if O(t) stucks, O(t+1) waits…

http://cs.brown.edu/courses/cs196-5/f12/handouts/async.pdf

Page 8: How NOT to write in Node.js

How software lives in hardware?

This is cool, software flow is predictibleBut not in high throughput I/O

Page 9: How NOT to write in Node.js

http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

Page 10: How NOT to write in Node.js

How software lives in hardware?

High throughput I/O doesn’t mean:

•Memory operations• Fast single-thread computing

Page 11: How NOT to write in Node.js

How software lives in hardware?

High throughput I/O means:

•HTTP requests•Database connections•Queue system dispatching•HDD operations

Page 12: How NOT to write in Node.js

Single-threaded, event loop model

Problem:

Imagine a man, who has a task:1. Walk around2. When bucket is full of water,

just pour another bucket3. Go to next bucket

http://www.nightmare.com/medusa/async_sockets.html

Page 13: How NOT to write in Node.js

Single-threaded, event loop model

What is nonblocking I/O?

Imagine a man, who has a task:1. Walk around2. When bucket is full of water,

just pour another bucket3. Go to next bucket

http://www.nightmare.com/medusa/async_sockets.html

Page 14: How NOT to write in Node.js

Single-threaded, event loop model

Problem:

Imagine a man, who has a task:1. Walk around2. When bucket is full of water,

just pour another bucket,if not… continue

3. Go to next buckethttp://www.nightmare.com/medusa/async_sockets.html

Page 15: How NOT to write in Node.js

Single-threaded, event loop model

How it is realised in low-level operating system?

•select()•/dev/pool descriptors•kqueue•pool•epool

Page 16: How NOT to write in Node.js

Single-threaded, event loop model

How it is realised in low-level operating system?

#include <sys/types.h>

#include <sys/socket.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds, const struct timeval *timeout);

Page 17: How NOT to write in Node.js

Single-threaded, event loop model

2. Avoid I/O blocking

Page 18: How NOT to write in Node.js

Node.js architecture

http://nodejs.org/logos/

Page 19: How NOT to write in Node.js

Node.js architecture

Node std library

node bindings (socket, http, …)

Event loop(libev)Google V8 Thread Pool

(libeio)

JavaScript

C/C++

Page 20: How NOT to write in Node.js

Node.js architecture

• Single-threadedno context switching

• Event loopno waits

• Javascript (Google V8)• ECMA-262 support

Page 21: How NOT to write in Node.js

Node.js architecture

http://www.tocadoelfo.com.br/2012/04/por-que-estou-aprendendo-nodejs.html

Page 22: How NOT to write in Node.js

ECMA-262 support

var arr = []

for(var i = 0, j = arr.length; i < j; ++i) {

var row = arr[i] // ufff…

}

var arr = []

arr.forEach(function(row) {

})

Page 23: How NOT to write in Node.js

„Parallelism”is a myth

Page 24: How NOT to write in Node.js

Node.js architecture

• Everything runs in parallel except your code

•When currently code is running,(not waiting for I/O descriptors)whole event loop is blocked

Page 25: How NOT to write in Node.js

„Parallelism”

• Let’s compute Fibonacci

function fib(n) {

return (n < 2) ? 1 : (fib(n-2)+fib(n-1));

}

This will simply block main (single) thread.

Page 26: How NOT to write in Node.js

„Parallelism”

•How about process.nextTick() ?

Page 27: How NOT to write in Node.js

„Parallelism”

•… but next iteration over function is delayed into next event loop iteration• That means each descriptor is checked before

computation.

Page 28: How NOT to write in Node.js

„Parallelism”

process.nextTick()to speed up computations is a myth

Node.js is not good solution to make single-process computations

Page 29: How NOT to write in Node.js

„Parallelism”

You can still fork another process

• threads_a_gogopool = require('threads_a_gogo').createPool(5)pool.any.eval('myFunction( ... )')

• Fork processvar fork = require('child_process').fork;var child = fork(__filename, [ 'arg1' ]);

Page 30: How NOT to write in Node.js

Callback hellis a mythwe can deal with it

Page 31: How NOT to write in Node.js

Callback hell

• Language construction – callback function•When operation is ready, call function passed as an

argument

someOperation(arg1, arg2, function() {

console.log('cool!');

})

Page 32: How NOT to write in Node.js

Callback hell

•When many operations are ready…

Page 33: How NOT to write in Node.js

var amqp = require('amqp');var connection = amqp.createConnection();

connection.on('ready', function() { connection.exchange("ex1", function(exchange) {

connection.queue('queue1', function(q) {q.bind(exchange, 'r1');

q.subscribe(function(json, headers, info, m) {console.log("msg: " +

JSON.stringify(json));});

});});

}); This is callback hell

Page 34: How NOT to write in Node.js

Callback hell

Deal with callback hell in many ways:

•Define callbacks as local variables•Use async module•Use PROMISE design pattern

Page 35: How NOT to write in Node.js

Callback hell

Define callbacks as local variables, like this:

var whenSthElseDone = function() {

// done...

}

var whenSthDone = function() {

operation(whenSthElseDone)

}

operation(whenSthDone)

Page 36: How NOT to write in Node.js

Callback hell

Use async module:

async.series([

function(callback) {

operation1(callback)

},

function(callback) {

operationBloking()

return callback()

}

],

function() {

// all done...

})

Page 37: How NOT to write in Node.js

Callback hell

PROMISE design pattern:

var Promise = require('promise');

var promise = new Promise(function (resolve, reject) {

operation1(function (err, res) {

if (err) reject(err);

else resolve(res);

});

});

Page 38: How NOT to write in Node.js

Callback hell

PROMISE design pattern:

promise.then(function(res) {

// done...

})

Page 39: How NOT to write in Node.js

Events (event bus)Triggering event does’t block whole event loop is a myth

Page 40: How NOT to write in Node.js

Eventsvar eventbus = require('events').EventEmitter

myFunction() {

operation1(function() {

eventbus.emit('myFunctionIsDone', {

result: 1234

})

}

}

Page 41: How NOT to write in Node.js

Eventsvar eventbus = require('events').EventEmitter

eventbus.on('myFunctionIsDone', function(data) {

console.log('cool! ')

})

Trigger waits until all listeners are done. So do not make long-running operations in event subscribers.

Page 42: How NOT to write in Node.js

Scaling EventEmitterTriggering events in event bus via EventEmitter does not scale!

Page 43: How NOT to write in Node.js

Scaling EventEmitter

• Triggering events in event bus via EventEmitterdoes not scale!• Events are visible locally in main thread

Page 44: How NOT to write in Node.js

Scaling EventEmitter

We need to use external queue system

to touch distributed nodes(3rd party for our application written in Node.js)

Page 45: How NOT to write in Node.js

Scaling EventEmitter

RabbitMQ is cool because:

•based on AMQP protocol(so integrates 3rd party software – an abstraction)•Queue async system•Publish-subscribe async model•Request-Response model

Page 46: How NOT to write in Node.js

Scaling EventEmitter

Emit:

1. Trigger event in your application via EventEmitter2. Catch it locally3. Re-trigger via RabbitMQ

Page 47: How NOT to write in Node.js

Scaling EventEmitter

Receive:

1. Subscribe on RabbitMQ exchange2. Trigger local event in application

Page 48: How NOT to write in Node.js

Scaling EventEmitter

This is only to separate responsibilitiesand good design of application

Page 49: How NOT to write in Node.js

Not only Node.jsNode.js is not a religion! It is only an implementation of async programming model

Page 50: How NOT to write in Node.js

Not only Node.js

Node.js is not a religion. Last time - hype.It only utilizes event-loop model, known from a long time…

• GUI frameworks• Tornado – Python• Linkedin parseq – Java• Async and Await – C#• Cramp – Ruby• reactphp – PHP, ehhh… WAT?!

Page 51: How NOT to write in Node.js

Event loop, single-threaded model is not solution for all your problems…Especially for long-running computings in single thread.It’s good for high throughput I/O.

Page 52: How NOT to write in Node.js

Thank you.Q&A?

Piotr [email protected]