how not to write in node.js

Post on 06-May-2015

6.492 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

How NOT to write in Node.js

Piotr Pelczarme@athlan.pl

PHPers, 6 Feb 2014

About me

Piotr Pelczarme@athlan.pl

About me

getfokus.com useselly.com

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

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

How software lives in hardware?

1. Avoid context switching = wasting time

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

How software lives in hardware?

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

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

How software lives in hardware?

High throughput I/O doesn’t mean:

•Memory operations• Fast single-thread computing

How software lives in hardware?

High throughput I/O means:

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

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

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

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

Single-threaded, event loop model

How it is realised in low-level operating system?

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

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

Single-threaded, event loop model

2. Avoid I/O blocking

Node.js architecture

http://nodejs.org/logos/

Node.js architecture

Node std library

node bindings (socket, http, …)

Event loop(libev)Google V8 Thread Pool

(libeio)

JavaScript

C/C++

Node.js architecture

• Single-threadedno context switching

• Event loopno waits

• Javascript (Google V8)• ECMA-262 support

Node.js architecture

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

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) {

})

„Parallelism”is a myth

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

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

„Parallelism”

•How about process.nextTick() ?

„Parallelism”

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

computation.

„Parallelism”

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

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

„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' ]);

Callback hellis a mythwe can deal with it

Callback hell

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

argument

someOperation(arg1, arg2, function() {

console.log('cool!');

})

Callback hell

•When many operations are ready…

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

Callback hell

Deal with callback hell in many ways:

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

Callback hell

Define callbacks as local variables, like this:

var whenSthElseDone = function() {

// done...

}

var whenSthDone = function() {

operation(whenSthElseDone)

}

operation(whenSthDone)

Callback hell

Use async module:

async.series([

function(callback) {

operation1(callback)

},

function(callback) {

operationBloking()

return callback()

}

],

function() {

// all done...

})

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

});

});

Callback hell

PROMISE design pattern:

promise.then(function(res) {

// done...

})

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

Eventsvar eventbus = require('events').EventEmitter

myFunction() {

operation1(function() {

eventbus.emit('myFunctionIsDone', {

result: 1234

})

}

}

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.

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

Scaling EventEmitter

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

Scaling EventEmitter

We need to use external queue system

to touch distributed nodes(3rd party for our application written 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

Scaling EventEmitter

Emit:

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

Scaling EventEmitter

Receive:

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

Scaling EventEmitter

This is only to separate responsibilitiesand good design of application

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

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

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.

Thank you.Q&A?

Piotr Pelczarme@athlan.pl

top related