The event driven, non blocking I/O library

About

{
    "firstName"	: "Bertrand",   
    "lastName"	: "Chevrier",
    "online" : {
        "twitter"   : "@kramp",
        "github"    : "github.com/krampstudio",
        "blog"      : "krampstudio.com"
    },
    "pro" : {
        "employer" : "Open Assessment Technologies",
        "position" : "JavaScript Developer",
        "project" : "TAO Testing (open source)" 
    },
    "loves" : [
        "JavaScript", "Linux", "Wine", "Horror films", "Fantasy Books"
    ]
}

JS, OSS & me

Grunt TAO
jQuery-UI
jQuery-UI

Follow the dealer

http://krampstudio.com
krampstudio.com
http://github.com/krampstudio
github.com/krampstudio

java js

Why Node.js

  1. Buzz platform
  2. JS.everywhere
  3. JIFSNIF
    JavaScript is fun so node is funnier
  4. Huge community
    Platform Repo Total packages Platform years old Repo years old Avg/year since repo
    Python PyPI 40 460 23 11 3 678
    Java Maven 71 916 19 10 7 194
    Ruby Gems 71 461 19 6 11 910
    Node Npm 61 681 6 4 15 170
    stats polled the 1st of March 2014

WTF is node.js ?

The illegitimate child of Google and Netscape !
from google
from netscape
rya

2008, Ryan Dahl

How to build scalable real-time web app with simplicity ?

Architecture

node.js isn't

  • A programming language
  • A web framework
  • The miracle that'll solve your performance issues

node.js is

  • A low level lib for I/O programming

node.js design

  • Non blocking I/O
  • Event driven model
  • JavaScript
  • Unix philosophy

Inspiration

  • Twisted (Python)
  • Tornado (Python)
  • EventMachine (Ruby)
  • libevent (C)
  • Nginx

Non blocking I/O

for scalability

Cost of I/O

I/O cycles
L1-cache 3
L2-cache 14
RAM 250
Disk 41 000 000
Network 240 000 000
http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop

Threaded server

threaded_server

Non blocking server

non_blocking_server

memory usage / concurrent connections

mem

request p. sec. / concurrent connections

rps
Node is designed to be non blocking from top to bottom.
  • Event driven model
  • Low levels I/O APIs rewritten
  • Fat process

JavaScript

for simplicity

Why JS?

JavaScript The Good Parts v8

The good parts for node

  • Functionnal language : fits the event model
  • Easily extensible : redesign of non blocking APIs, DSLable
  • Becomes popular : the language of the web

Unix philosophy

KISS & Battery not included

Node provides only low level APIs

Nothing superfluous.

EcmaScript 5 + CommonJs + 32 APIs:

  • System (Process, Cluster, Os, Path, VM, etc.)
  • I/O (Http, UDP, FileSystem, etc.)
  • Framework (Event, Domain, Streams, etc.)
  • Utils (REPL, NPM, debug, etc.)

nodejs.org/api

JavaScript (ES5) file import ?

desert

CommonJs

Modules


module.exports = {
	//your object
};
						

Require


var fs = require('fs');
						
node architecture

npm

Node Package Manager

NPM

manages packages as CommonJS modules

How ?

Module Metas

package.json

{
    "name": "node-htop",
    "version": "0.1.0",
    "description": "Web htop like",
    "main": "app.js",
    "scripts": {
        "test": "grunt test"
    },
    "dependencies": {
        "express": "3.2.x",
        "socket.io": "0.9.x",
        "underscore": "~1.4.0"
    }
}
                        
demo package.json

Usage

$ npm search bower
$ npm info bower
$ npm install -g bower

$ npm install --save socket.io
					    

$ npm update
					    

$ npm publish
					    

Scopes

global                       /usr/lib/node_modules/ 
    -> user                  $USER/.npm/
        -> project           $PROJECT/node_modules/
normal : always installed
dev    : installed by npm install but not as a dependency
peer   : works along with only with a  module (plugin)

Transitive dependencies

A-0.1 -> B-0.1, C-0.1
B-01 -> C-0.2
                        
                        
A-0.1
├── node_modules
│   ├── B-0.1
│   │   └── node_modules
│   │       └── C-0.2
│   └── C-0.1
└── package.json
                        
Why does npm hate me?
npm is not capable of hatred. It loves everyone, especially you. source npm faq

Diving into node

Coding with node

Discovering the patterns

Required Hello Node.js world


var http = require("http");

http.createServer(function(request, response) {

  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello World");
  response.end();
  
}).listen(8888);
					    

$ node hello_node_world.js
					    

The CommonJS way 1/3


//foo.js
exports.bar = function(){
    
}
//or
module.exports = {
    bar : function(){

    }
};
                        

var foo = require('./foo')
foo.bar();
                        

The CommonJS way 2/3


function FooBared(){

}
FooBared.prototype.doit = function (){

};

module.exports = Foobared;
                        

var Foobared = require('./foobared')
var fooBar = new Foobared();
fooBar.doit();
                        

The CommonJS way 3/3


var cache = 0;
cache++;
exports.get = function(){
    return cache;
};
                        

var value1 = require('./cached').get();
console.log(value1);
var value2 = require('./cached').get();
console.log(value2);
                        

Callbacks, Errorbacks


function getUserName(id, cb) {
    if (!id) {
        return cb(new Error('Id is required'));
    }
    db.query({id : id}, function(err, user){	
  	    cb(null, user.name);
    });
};
                        

getUserName(12, function(err, name){
	if(err){
        console.error(err);
    }
    console.log("Hello " + name);
});
                        

Callback tree


http.createServer(function processRequest(request, response){
    fs.exists("/tmp/test", function(exists){
        if(exists === true){
            fs.stat(file, function(err, stat){
                fs.appendFile(file, log, 'utf8', function(err){
                    db.query(function(err, data){
                        //etc ...
                    });
                });
            });
        }
    });
}
						 

Good practices


function getUserName(id, gotUserName) {
    if (!id) {
        return gotUserName(new Error('Id is required'));
    }
    db.query({id : id}, function queryResult (err, user){	
        if(err){
            return gotUserName(err);
        }
  	    return gotUserName(null, user.name);
    });
};
                        

getUserName(12, function gotUserName (err, name){
	if(err){
        return console.error(err);
    }
    return console.log("Hello " + name);
});
                        

Async management


async.filter(['file1','file2','file3'], fs.exists, function filesExist(results){
   console.log(results); //true, false, true]
});

function f1(cb){
    fs.exists('dir1', function(result){
        if(result === true){
            cb(null, true);
        } else {
            cb(new Error('Oh crap!');
        }
    });
}

async.series([f1, f2], function(err, results){
   // results => [true, true] or null
});
                        

Futures and promises


var q = require('q'), fs = require('fs');                     

//wrap
var readFile = q.node(fs.readFile);

//call
readFile('test.txt')
    .then(function (data) {
        console.log("finished");
    })
    .then(function(){
        console.log("do something else");
    });

                        

Events


var events = require('events');
var Test = function(){
	events.EventEmitter.call(this);
};
util.inherits(Test, events.EventEmitter);

Test.prototype.start = function(){
	this.emit('started', { 'when' : new Date() });
};
						

var myTest = new Test();
myTest.on('started', function(data){
	console.log("test started");
});
myTest.start();
						

Middleware


var app = {
    stack : [],
    use : function(middleware){
        var fn = middleware;
        if('function' === typeof middleware.handle){
            fn = middleware.handle;
        }
        if('function' === typeof fn){
            this.stack.push(fn);
        }
    },

    handle : function(data){
        var i = 0, layer;
        for(i in this.stack){
            layer = this.stack[i];
            if(!layer(data)){
                break;
            }
        }
    }
};
                        

app.use(function(data){
    console.log("Hello %s", data);
    return  true;
});
app.use(function(data){
    console.error("Goodbye %s", data);
    return false;
});
app.handle('middleware');
                        

Connect : HTTP middlewares


var app = connect()
  .use(connect.logger('dev'))
  .use(connect.static('public'))
  .use(function(req, res){
    res.end('hello world\n');
  })
 .listen(3000);
                        

Errors


try {
    //something that crash
} catch(e) {
    console.error(e);
};

function(cb){
    if(error)
        cb(new Error("Sorry..."));
    } else {
        cb(null, theData);
    }
}

myEmitter.on('error', function(e){
    console.error(e.stack);
});

var aDomain = domain.create();
aDomain.on('error', function(err){
    //do wafyw with the err
});

aDomain.run(function(){
    //do some errorable stuffs (throws, errbacks, etc. are catched)
});

Streams


var stream = request("http://nodestreams.com/input/people.json.gz") 
    .pipe(zlib.createGunzip())                          
    .pipe(new Iconv("ISO-8859-1", "UTF-8"));           

stream.pipe(fs.createWriteStream("output/people.json")); 
stream.pipe(process.stdout)

Scaling node.js

But, wait, I've only one process...

Scale Up

Use your CPUs !

Cluster


                        var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  for (var i = 0; i > numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    //manage worker stop
  });
  
} else {
    //I'm in a fork
}                        

Scale out

Load balance and share the events
scaling architecture

Node.js is fun but...

Defects

  • Young project
  • Libraries hell
  • Error management
  • Monoculture
  • Noob friendly
  • Devops required
Node is a cancer
Ted Dzubia
JavaScript is a Toy!

Coffescript invokes the :

bear
beard theory
With great power, comes great responsibility
Uncle Ben, Spiderman

Node.js ecosystem

Platform

+

Great projects


Node everywhere