KEMBAR78
Node.js - async for the rest of us. | PDF
node.js
       asynchronous...
      for the rest of us




     Mike Brevoort
       8.2.2011
         DOSUG
           code sample can be found at
https://github.com/mbrevoort/node.js-presentation
agenda

the case for node.js
developing with node
look at a few popular modules
lessons from the trenches
typical n-tier
         run-a-round
     browser makes call to web server
     (waits)
     web server makes call to database
     (waits)
     web server returns result to browser



Response time is dominated by time waiting
typical i/o latency
                      L1: 3 cycles
                     L2: 14 cycles
                    RAM: 250 cycles
                DISK: 41,000,000 cycles
              NETWORK: 240,000,000 cycles
     L1
     L2
   RAM
   Disk
Network

          0     60,000,000   120,000,000   180,000,000   240,000,000     300,000,000
                                                               http://nodejs.org/jsconf.pdf
http://xach.livejournal.com/
        170311.html
“Most languages were designed to
solve computational problems, but
      Node.js is different.

   Node.js was designed from the
ground up to efficiently handle the
communication that is at the heart
   of modern web applications.”


                    http://www.joyentcloud.com/products/smart-appliances/
                                    node-js-smartmachine/
node.js
An Evented I/O network server
for Javascript


       created by   sponsored by
       Ryan Dahl
Runs Javascript, but isn’t
   primarily Javascript




               http://platformjs.wordpress.com/2010/11/24/node-js-under-
                                       the-hood/
Why Javascript?
most widely used programing
language of the web
“never under estimate the power of familiarity and
friendliness” - Stacey Higginbotham, GigaOM


async by nature - the browser
is a single threaded event
loop

         BAH! It’s a toy language!
the event loop
single threaded = no execution
concurrency
all execution initiated by an event
events may have zero to many
callbacks
events are executed in order
Tom Hughes-Croucher’s postman
analogy
Installing node
mac, linux or windows (with cygwin hell)

fear not! stable windows support coming
in v0.6.0 (~3wks)
    # clone the git repo
    git clone git://github.com/joyent/node.git
    cd node

    # checkout the version you want
    git checkout v0.4.10

    # build and install
    ./configure
    make && sudo make install
Running Node
                       ~/ node
                       > var x=1, y=2, z=3;
                       > x+y+z

      REPL
                       6
                       > require('fs').statSync('./blocking.js')
                       { dev: 234881026,
Read-Eval-Print-Loop     ino: 3162208,
                         mode: 33188,
                         nlink: 1,
                       .....




                       node app.js arg1 arg2


Invoke script          process.argv[0]
                       process.argv[1]
                                         ===
                                         ===
                                               "node"
                                               "app.js"
                       process.argv[2]   ===   "arg1"
                       process.argv[3]   ===   "arg2"
Hello World HTTP
           Server

var http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello Worldn');
}).listen(8080, "127.0.0.1");

console.log('Server running at http://127.0.0.1:8080/');
Not just HTTP
var net = require('net');

var server = net.createServer(function (socket) {
  socket.write("Echo serverrn");
  socket.pipe(socket);
});

server.listen(1337, "127.0.0.1");




                                             http://nodejs.org/
simple irc demo
Developing with
     Node

install node
install npm
your favorite text editor
 Sublime Text 2, Textmate, vim,
 Emacs, Eclipse, whatever
npm
node package manager



       created by
       Isaac Schlueter (Joyent)
npm

publish, install, discover,
and develop node programs
puts modules in a place where
node can find them
manages dependencies
npm search, install
 # install a module (copy to node_modules)
 npm install socket.io

 # install a specific version
 npm install socket.io@0.6.0

 # install module globally
 npm install socket.io -g

 # search
 npm search socket

 #info
 npm info socket.io
npm registry



         http://registry.npmjs.org/
npmjs.org stats
{
    "name": "express",
                                 package.json
    "description": "Sinatra inspired web development framework",
    "version": "3.0.0",
    "author": "TJ Holowaychuk <tj@vision-media.ca>",
    "contributors": [
       { "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
       { "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" },
       { "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" },
       { "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
    ],
    "dependencies": {
       "connect": ">= 1.5.2 < 2.0.0",
       "mime": ">= 0.0.1",
       "qs": ">= 0.3.0"
    },
    "devDependencies": {
       "connect-form": "0.2.1",
       "ejs": "0.4.2",
       "expresso": "0.8.1",
       "hamljs": "0.5.1",
       "jade": "0.13.0",
       "stylus": "0.13.0",
       "should": "0.2.1",
       "express-messages": "0.0.2",
       "node-markdown": ">= 0.0.1",
       "connect-redis": ">= 0.0.1"
    },
    "keywords": ["framework", "sinatra", "web", "rest", "restful"],
    "repository": "git://github.com/visionmedia/express",
    "main": "index",
    "bin": { "express": "./bin/express" },
    "scripts": {
       "test": "make test",
       "prepublish" : "npm prune"
    },
    "engines": { "node": ">= 0.4.9 < 0.7.0" }
}
npm install .
package.json isn’t just for
modules published to npm
npm can help you manage and
install dependencies in any
project

  # from the same directory
  # as package.json
  npm install .
npm list
~/ npm list
my_project@0.1.0 /Users/mikebre/my_project
!"# cluster@0.6.9 invalid
$ %"" log@1.2.0
!"# connect-gzip@0.1.0
$ !"# connect@1.4.6
$ $ %"" qs@0.1.0
$ %"" mime@1.2.2
!"# date@1.0.1
$ %"" require-kiss@1.0.5
!"" docco@0.3.0 extraneous
!"# express@2.3.2
$ !"" connect@1.4.6
$ !"" mime@1.2.2
$ %"" qs@0.1.0
!"" hbs@0.0.7
!"" log@1.1.0 extraneous
!"" metrics@0.1.1
!"" request-forked@1.9.8
!"" semver@1.0.6 invalid
%"# xml2js@0.1.6
  %"" sax@0.1.4
a very strong
    community
nodejs.org
Google Group mailing list
IRC #node.js on freenode
Stack Overflow, LinkedIn groups
nodeconf, node summercamp, etc.
debugging

ndb - command
line debugger
Eclipse debugger
plugin for V8
node-inspector
is very nice!
node-inspector
         uses WebKit Web Inspector


# install with npm
npm -g install node-inspector

# start node-inspector
node-inspector &

# start node in debug mode
node --debug app.js
profiling
node-inspector optionally
supports the V8 profiler
collects CPU and heap
snapshots



    Speaking of heaps...
garbage collection
 --trace-gc option to watch GC
 behavior
 V8 is a VM --> must GC
 tuned for the browser
  20Mb - 40Mb per tab
 Large node heap sizes == :(
GC Demo
several popular node
       modules




       http://splashinthepacific.files.wordpress.com/2010/10/looking-glass-721.jpg
Express
Sinatra (Ruby) inspired web
framework


       created by
       TJ Holowaychuk
Express
request routing
content negotiation
view templating and partials
session support
static file serving
fast, clean and powerful
Express Demo
a RESTful service?
cute, terse. boring




let’s do something a
bit more interesting...
streaming demo
Socket.io
Unified API for Websockets +
fallbacks

       created by
       Guillermo Rauch
Socket.io
unified API for Comet style apps
transport negotiation
server and client libraries
feature rich, above and beyond what
the websocket protocol prescribes
 heartbeats, timeouts, namespacing,
 volatile messages, message
 acknowledgements, etc.
socket.io demo
Lessons
from the trenches
asynchronous
      learning curve
          app.get('/bar', function(req, res) {
              foo.fetchSomething(function(error, something) {
                  if(!error) {
                      foo.fetchSomeOne(something, function(error, someone) {

easy to                   if(!error) {
                               foo.fetchBar(function(error, bar) {
                                   if(!error) {
 write                             }
                                       res.send("we got bar: " + bar);


 code                              else {
                                       res.send(error.statusCode);
                                   }

 like                     }
                               });



 this
                          else {
                               res.send(error.statusCode);
                          }
                      });
                  }
                  else {
                      res.send(error.statusCode);
                  }
              });
          });
uncaught errors
      on error, node emits an ‘error’ event on
      the corresponding object
      if no listeners on object for ‘error’, a
      top level exception is raised and the
      process exits




           >
               var server = http.createServer(function (req, res) {});
prudent        server.on('error', function(error) {
approach           console.log("Caught error! Don't exit!");
               });




nuclear
approach   >   process.on('uncaughtException', function(error) {

               });
                   console.log("Kaboom.... handle " + error);
plan for multiple
processes from the start
  each node process is bound to one core
  many small processes better than one
  big one
  use Cluster
  https://github.com/learnboost/cluster




         var cluster = require('cluster');

         cluster('app.js')
             .set('workers', 16) // defaults to # of cores
             .use(cluster.logger('logs'))
             .use(cluster.stats())
             .use(cluster.cli())
             .use(cluster.repl(8888))
             .listen('80')
keep the heap small
 200Mb or less if you’re worried
 about GC pause
 move data out of the node process
  instead use Redis, MongoDb, etc
  encourages statelessness,
  encourages scalability
  reduces risk of losing a single
  node process
everything you do
     blocks
     No CPU for YOU!




                       http://redriverpak.files.wordpress.com/
                          2010/08/vwtouareg-road-block.jpg
be weary of loops
 for (var i=0, l=entries.length; i<l; i++) {
     doSomething(entries[i]);
 }



         innocent enough?


     if # entries = 10,000

  doSomething() takes ~1ms

 you block for 10 seconds!
non-blocking loops
 // order matters
 function processEntry(entries, index) {
     index = index || 0;
     if(index === entries.length) return done();

     doSomething(entries[index]);
     process.nextTick(function() {
         processEntry(entries, index++)
     });
 }

 processEntry(entries);
non-blocking loops
 // order doesn't matter
 var leftToProcess = entries.length;

 for (var i=0, l=entries.length; i<l; i++) {
      (function(foo) {
          process.nextTick(function() {
             doSomething(foo);
             if(--leftToProcess === 0) {
                 done();
             }
         });
      })(entries[i]);
 }
non-blocking loops
 // order doesn't matter
 // doSomething takes callback and is Async
 // doSomethingAsync's happen in parallel
 var leftToProcess = entries.length;

 // doSomething's will be executed in parallel
 for (var i=0, l=entries.length; i<l; i++) {
      (function(foo) {
          process.nextTick(function() {
             doSomethingAsync(foo, function() {
                 if(--leftToProcess === 0) {
                      done();
                 }
             });
         });
      })(entries[i]);
 }
set ulimit -n
node can handles 1000’s of connections?
  but your OS says...
  Too many open files
default # file descriptors on most linux
systems is 1024
1 FD per socket means max open sockets < 1024
 increase the max # of file descriptors
  ulimit -n <max # FD>
  ulimit -a to see current max
pooled outbound
      connections
node pools outbound http(s) connections by default
for host + port combinations
default concurrent maxSockets per host + port is 5
is this what you want?


// for http as of node v0.4.10
require ('http')
    .getAgent("api.twitter.com", 80)
    .maxSockets = 100;

// for https as of node v0.4.10
require ('https')
    .getAgent({ host:"docs.google.com", port: 443 })
    .maxSockets = 100;
timeouts
expect that any callback could fail and
may not be called
anticipate conditions where both
inbound or outbound connections may
hang
use Mikeal Roger’s ‘request’ module
 I contributed timeout functionality
 should be part of node core ~v0.7/0.8
offload anything
computationally intensive

  spawn a child process
  require('child_process').spawn


  call out to another system
  more apt to handle heavy
  lifting
  use a job queue
be specific with
package dependencies
{
    "name": "Foo Package",
    "description": "my Foo package",




                           >
    "version": "1.0.0",
    "author": "Mike Brevoort <mikebre@ecollege.com>",
    "dependencies": {
       "express": "2.3.2",
       "cluster": ">= 0.6.1",              is this what
       "mongodb": "0.9.x",
       "connect-gzip": "~0.1",
                                           you really
       "underscore": "= latest"            want? really?
    },
    "engines": { "node": "= 0.4.8" }
}
Let me tell you about my
         friend node



he’s a great multi-tasker but
 can only do one thing at a
             time
Thank You!
Questions?

   Mike Brevoort
   @mbrevoort
   mike [at] brevoort [dot] com

Node.js - async for the rest of us.

  • 1.
    node.js asynchronous... for the rest of us Mike Brevoort 8.2.2011 DOSUG code sample can be found at https://github.com/mbrevoort/node.js-presentation
  • 2.
    agenda the case fornode.js developing with node look at a few popular modules lessons from the trenches
  • 4.
    typical n-tier run-a-round browser makes call to web server (waits) web server makes call to database (waits) web server returns result to browser Response time is dominated by time waiting
  • 5.
    typical i/o latency L1: 3 cycles L2: 14 cycles RAM: 250 cycles DISK: 41,000,000 cycles NETWORK: 240,000,000 cycles L1 L2 RAM Disk Network 0 60,000,000 120,000,000 180,000,000 240,000,000 300,000,000 http://nodejs.org/jsconf.pdf
  • 6.
  • 7.
    “Most languages weredesigned to solve computational problems, but Node.js is different. Node.js was designed from the ground up to efficiently handle the communication that is at the heart of modern web applications.” http://www.joyentcloud.com/products/smart-appliances/ node-js-smartmachine/
  • 8.
    node.js An Evented I/Onetwork server for Javascript created by sponsored by Ryan Dahl
  • 9.
    Runs Javascript, butisn’t primarily Javascript http://platformjs.wordpress.com/2010/11/24/node-js-under- the-hood/
  • 10.
    Why Javascript? most widelyused programing language of the web “never under estimate the power of familiarity and friendliness” - Stacey Higginbotham, GigaOM async by nature - the browser is a single threaded event loop BAH! It’s a toy language!
  • 11.
    the event loop singlethreaded = no execution concurrency all execution initiated by an event events may have zero to many callbacks events are executed in order Tom Hughes-Croucher’s postman analogy
  • 12.
    Installing node mac, linuxor windows (with cygwin hell) fear not! stable windows support coming in v0.6.0 (~3wks) # clone the git repo git clone git://github.com/joyent/node.git cd node # checkout the version you want git checkout v0.4.10 # build and install ./configure make && sudo make install
  • 13.
    Running Node ~/ node > var x=1, y=2, z=3; > x+y+z REPL 6 > require('fs').statSync('./blocking.js') { dev: 234881026, Read-Eval-Print-Loop ino: 3162208, mode: 33188, nlink: 1, ..... node app.js arg1 arg2 Invoke script process.argv[0] process.argv[1] === === "node" "app.js" process.argv[2] === "arg1" process.argv[3] === "arg2"
  • 14.
    Hello World HTTP Server var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello Worldn'); }).listen(8080, "127.0.0.1"); console.log('Server running at http://127.0.0.1:8080/');
  • 15.
    Not just HTTP varnet = require('net'); var server = net.createServer(function (socket) { socket.write("Echo serverrn"); socket.pipe(socket); }); server.listen(1337, "127.0.0.1"); http://nodejs.org/
  • 16.
  • 17.
    Developing with Node install node install npm your favorite text editor Sublime Text 2, Textmate, vim, Emacs, Eclipse, whatever
  • 18.
    npm node package manager created by Isaac Schlueter (Joyent)
  • 19.
    npm publish, install, discover, anddevelop node programs puts modules in a place where node can find them manages dependencies
  • 20.
    npm search, install # install a module (copy to node_modules) npm install socket.io # install a specific version npm install socket.io@0.6.0 # install module globally npm install socket.io -g # search npm search socket #info npm info socket.io
  • 21.
    npm registry http://registry.npmjs.org/
  • 22.
  • 23.
    { "name": "express", package.json "description": "Sinatra inspired web development framework", "version": "3.0.0", "author": "TJ Holowaychuk <tj@vision-media.ca>", "contributors": [ { "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" }, { "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" }, { "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" }, { "name": "Guillermo Rauch", "email": "rauchg@gmail.com" } ], "dependencies": { "connect": ">= 1.5.2 < 2.0.0", "mime": ">= 0.0.1", "qs": ">= 0.3.0" }, "devDependencies": { "connect-form": "0.2.1", "ejs": "0.4.2", "expresso": "0.8.1", "hamljs": "0.5.1", "jade": "0.13.0", "stylus": "0.13.0", "should": "0.2.1", "express-messages": "0.0.2", "node-markdown": ">= 0.0.1", "connect-redis": ">= 0.0.1" }, "keywords": ["framework", "sinatra", "web", "rest", "restful"], "repository": "git://github.com/visionmedia/express", "main": "index", "bin": { "express": "./bin/express" }, "scripts": { "test": "make test", "prepublish" : "npm prune" }, "engines": { "node": ">= 0.4.9 < 0.7.0" } }
  • 24.
    npm install . package.jsonisn’t just for modules published to npm npm can help you manage and install dependencies in any project # from the same directory # as package.json npm install .
  • 25.
    npm list ~/ npmlist my_project@0.1.0 /Users/mikebre/my_project !"# cluster@0.6.9 invalid $ %"" log@1.2.0 !"# connect-gzip@0.1.0 $ !"# connect@1.4.6 $ $ %"" qs@0.1.0 $ %"" mime@1.2.2 !"# date@1.0.1 $ %"" require-kiss@1.0.5 !"" docco@0.3.0 extraneous !"# express@2.3.2 $ !"" connect@1.4.6 $ !"" mime@1.2.2 $ %"" qs@0.1.0 !"" hbs@0.0.7 !"" log@1.1.0 extraneous !"" metrics@0.1.1 !"" request-forked@1.9.8 !"" semver@1.0.6 invalid %"# xml2js@0.1.6 %"" sax@0.1.4
  • 26.
    a very strong community nodejs.org Google Group mailing list IRC #node.js on freenode Stack Overflow, LinkedIn groups nodeconf, node summercamp, etc.
  • 27.
    debugging ndb - command linedebugger Eclipse debugger plugin for V8 node-inspector is very nice!
  • 28.
    node-inspector uses WebKit Web Inspector # install with npm npm -g install node-inspector # start node-inspector node-inspector & # start node in debug mode node --debug app.js
  • 29.
    profiling node-inspector optionally supports theV8 profiler collects CPU and heap snapshots Speaking of heaps...
  • 30.
    garbage collection --trace-gcoption to watch GC behavior V8 is a VM --> must GC tuned for the browser 20Mb - 40Mb per tab Large node heap sizes == :(
  • 31.
  • 32.
    several popular node modules http://splashinthepacific.files.wordpress.com/2010/10/looking-glass-721.jpg
  • 33.
    Express Sinatra (Ruby) inspiredweb framework created by TJ Holowaychuk
  • 34.
    Express request routing content negotiation viewtemplating and partials session support static file serving fast, clean and powerful
  • 35.
  • 36.
    a RESTful service? cute,terse. boring let’s do something a bit more interesting...
  • 37.
  • 38.
    Socket.io Unified API forWebsockets + fallbacks created by Guillermo Rauch
  • 39.
    Socket.io unified API forComet style apps transport negotiation server and client libraries feature rich, above and beyond what the websocket protocol prescribes heartbeats, timeouts, namespacing, volatile messages, message acknowledgements, etc.
  • 40.
  • 41.
  • 42.
    asynchronous learning curve app.get('/bar', function(req, res) { foo.fetchSomething(function(error, something) { if(!error) { foo.fetchSomeOne(something, function(error, someone) { easy to if(!error) { foo.fetchBar(function(error, bar) { if(!error) { write } res.send("we got bar: " + bar); code else { res.send(error.statusCode); } like } }); this else { res.send(error.statusCode); } }); } else { res.send(error.statusCode); } }); });
  • 43.
    uncaught errors on error, node emits an ‘error’ event on the corresponding object if no listeners on object for ‘error’, a top level exception is raised and the process exits > var server = http.createServer(function (req, res) {}); prudent server.on('error', function(error) { approach console.log("Caught error! Don't exit!"); }); nuclear approach > process.on('uncaughtException', function(error) { }); console.log("Kaboom.... handle " + error);
  • 44.
    plan for multiple processesfrom the start each node process is bound to one core many small processes better than one big one use Cluster https://github.com/learnboost/cluster var cluster = require('cluster'); cluster('app.js') .set('workers', 16) // defaults to # of cores .use(cluster.logger('logs')) .use(cluster.stats()) .use(cluster.cli()) .use(cluster.repl(8888)) .listen('80')
  • 45.
    keep the heapsmall 200Mb or less if you’re worried about GC pause move data out of the node process instead use Redis, MongoDb, etc encourages statelessness, encourages scalability reduces risk of losing a single node process
  • 46.
    everything you do blocks No CPU for YOU! http://redriverpak.files.wordpress.com/ 2010/08/vwtouareg-road-block.jpg
  • 47.
    be weary ofloops for (var i=0, l=entries.length; i<l; i++) { doSomething(entries[i]); } innocent enough? if # entries = 10,000 doSomething() takes ~1ms you block for 10 seconds!
  • 48.
    non-blocking loops //order matters function processEntry(entries, index) { index = index || 0; if(index === entries.length) return done(); doSomething(entries[index]); process.nextTick(function() { processEntry(entries, index++) }); } processEntry(entries);
  • 49.
    non-blocking loops //order doesn't matter var leftToProcess = entries.length; for (var i=0, l=entries.length; i<l; i++) { (function(foo) { process.nextTick(function() { doSomething(foo); if(--leftToProcess === 0) { done(); } }); })(entries[i]); }
  • 50.
    non-blocking loops //order doesn't matter // doSomething takes callback and is Async // doSomethingAsync's happen in parallel var leftToProcess = entries.length; // doSomething's will be executed in parallel for (var i=0, l=entries.length; i<l; i++) { (function(foo) { process.nextTick(function() { doSomethingAsync(foo, function() { if(--leftToProcess === 0) { done(); } }); }); })(entries[i]); }
  • 51.
    set ulimit -n nodecan handles 1000’s of connections? but your OS says... Too many open files default # file descriptors on most linux systems is 1024 1 FD per socket means max open sockets < 1024 increase the max # of file descriptors ulimit -n <max # FD> ulimit -a to see current max
  • 52.
    pooled outbound connections node pools outbound http(s) connections by default for host + port combinations default concurrent maxSockets per host + port is 5 is this what you want? // for http as of node v0.4.10 require ('http') .getAgent("api.twitter.com", 80) .maxSockets = 100; // for https as of node v0.4.10 require ('https') .getAgent({ host:"docs.google.com", port: 443 }) .maxSockets = 100;
  • 53.
    timeouts expect that anycallback could fail and may not be called anticipate conditions where both inbound or outbound connections may hang use Mikeal Roger’s ‘request’ module I contributed timeout functionality should be part of node core ~v0.7/0.8
  • 54.
    offload anything computationally intensive spawn a child process require('child_process').spawn call out to another system more apt to handle heavy lifting use a job queue
  • 55.
    be specific with packagedependencies { "name": "Foo Package", "description": "my Foo package", > "version": "1.0.0", "author": "Mike Brevoort <mikebre@ecollege.com>", "dependencies": { "express": "2.3.2", "cluster": ">= 0.6.1", is this what "mongodb": "0.9.x", "connect-gzip": "~0.1", you really "underscore": "= latest" want? really? }, "engines": { "node": "= 0.4.8" } }
  • 56.
    Let me tellyou about my friend node he’s a great multi-tasker but can only do one thing at a time
  • 57.
    Thank You! Questions? Mike Brevoort @mbrevoort mike [at] brevoort [dot] com

Editor's Notes