node.js - async for the rest of us
DESCRIPTION
presented at the Denver Open Source Users Group 8/2/2011TRANSCRIPT
node.jsasynchronous...
for the rest of us
Mike Brevoort8.2.2011DOSUG
code sample can be found at https://github.com/mbrevoort/node.js-presentation
agenda
the case for node.jsdeveloping with node
look at a few popular moduleslessons 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 latencyL1: 3 cyclesL2: 14 cycles
RAM: 250 cyclesDISK: 41,000,000 cycles
NETWORK: 240,000,000 cycles
http://nodejs.org/jsconf.pdf
L1
L2
RAM
Disk
Network
0 60,000,000 120,000,000 180,000,000 240,000,000 300,000,000
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.jsAn Evented I/O network server for Javascript
created by Ryan Dahl
sponsored by
http://platformjs.wordpress.com/2010/11/24/node-js-under-the-hood/
Runs Javascript, but isn’t primarily Javascript
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 loopsingle threaded = no execution concurrencyall execution initiated by an eventevents may have zero to many callbacksevents are executed in orderTom Hughes-Croucher’s postman analogy
Installing nodemac, linux or windows (with cygwin hell)
fear not! stable windows support coming in v0.6.0 (~3wks)
# clone the git repogit clone git://github.com/joyent/node.gitcd node
# checkout the version you wantgit checkout v0.4.10
# build and install./configuremake && sudo make install
Running Node
REPL Read-Eval-Print-Loop
Invoke scriptnode app.js arg1 arg2
process.argv[0] === "node"process.argv[1] === "app.js"process.argv[2] === "arg1"process.argv[3] === "arg2"
~/ node> var x=1, y=2, z=3;> x+y+z6> require('fs').statSync('./blocking.js'){ dev: 234881026, ino: 3162208, mode: 33188, nlink: 1,.....
Hello World HTTP Server
var http = require('http');
http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(8080, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8080/');
Not just HTTPvar net = require('net');
var server = net.createServer(function (socket) { socket.write("Echo server\r\n"); socket.pipe(socket);});
server.listen(1337, "127.0.0.1");
http://nodejs.org/
simple irc demo
Developing with Node
install nodeinstall npmyour favorite text editorSublime Text 2, Textmate, vim, Emacs, Eclipse, whatever
npmnode package manager
created by Isaac Schlueter (Joyent)
npm
publish, install, discover, and develop node programsputs modules in a place where node can find themmanages dependencies
npm search, install# install a module (copy to node_modules)npm install socket.io
# install a specific versionnpm install [email protected]
# install module globallynpm install socket.io -g
# searchnpm search socket
#infonpm info socket.io
npm registry
http://registry.npmjs.org/
npmjs.org stats
package.json{ "name": "express", "description": "Sinatra inspired web development framework", "version": "3.0.0", "author": "TJ Holowaychuk <[email protected]>", "contributors": [ { "name": "TJ Holowaychuk", "email": "[email protected]" }, { "name": "Aaron Heckmann", "email": "[email protected]" }, { "name": "Ciaran Jessup", "email": "[email protected]" }, { "name": "Guillermo Rauch", "email": "[email protected]" } ], "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 [email protected] /Users/mikebre/my_project!"# [email protected] invalid$ %"" [email protected] !"# [email protected] $ !"# [email protected] $ $ %"" [email protected] $ %"" [email protected] !"# [email protected] $ %"" [email protected] !"" [email protected] extraneous!"# [email protected] $ !"" [email protected] $ !"" [email protected] $ %"" [email protected] !"" [email protected] !"" [email protected] extraneous!"" [email protected] !"" [email protected] !"" [email protected] invalid%"# [email protected] %"" [email protected]
a very strong community
nodejs.orgGoogle Group mailing listIRC #node.js on freenodeStack Overflow, LinkedIn groupsnodeconf, node summercamp, etc.
debugging
ndb - command line debuggerEclipse debugger plugin for V8node-inspectoris very nice!
node-inspectoruses WebKit Web Inspector
# install with npmnpm -g install node-inspector
# start node-inspectornode-inspector &
# start node in debug modenode --debug app.js
profilingnode-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 browser20Mb - 40Mb per tab
Large node heap sizes == :(
GC Demo
several popular node modules
http://splashinthepacific.files.wordpress.com/2010/10/looking-glass-721.jpg
ExpressSinatra (Ruby) inspired web framework
created by TJ Holowaychuk
Expressrequest routingcontent negotiationview templating and partialssession supportstatic file servingfast, clean and powerful
Express Demo
a RESTful service?cute, terse. boring
let’s do something a bit more interesting...
streaming demo
Socket.ioUnified API for Websockets + fallbacks
created by Guillermo Rauch
Socket.iounified API for Comet style appstransport negotiation server and client librariesfeature rich, above and beyond what the websocket protocol prescribesheartbeats, 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) { if(!error) { foo.fetchBar(function(error, bar) { if(!error) { res.send("we got bar: " + bar); } else { res.send(error.statusCode); } }); } else { res.send(error.statusCode); } }); } else { res.send(error.statusCode); } }); });
easy to write code like this
uncaught errorson error, node emits an ‘error’ event on the corresponding objectif no listeners on object for ‘error’, a top level exception is raised and the process exits
process.on('uncaughtException', function(error) { console.log("Kaboom.... handle " + error);});
var server = http.createServer(function (req, res) {});server.on('error', function(error) { console.log("Caught error! Don't exit!");});
prudentapproach
nuclearapproach
>>
plan for multiple processes from the start
each node process is bound to one coremany small processes better than one big oneuse Clusterhttps://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 small200Mb or less if you’re worried about GC pausemove data out of the node processinstead use Redis, MongoDb, etcencourages statelessness, encourages scalabilityreduces risk of losing a single node process
everything you do blocksNo CPU for YOU!
http://redriverpak.files.wordpress.com/2010/08/vwtouareg-road-block.jpg
be weary of loopsfor (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 mattersfunction 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 mattervar 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 parallelfor (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 -nnode can handles 1000’s of connections?
but your OS says... Too many open files
default # file descriptors on most linux systems is 10241 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 combinationsdefault concurrent maxSockets per host + port is 5is this what you want?
// for http as of node v0.4.10require ('http') .getAgent("api.twitter.com", 80) .maxSockets = 100;
// for https as of node v0.4.10require ('https') .getAgent({ host:"docs.google.com", port: 443 }) .maxSockets = 100;
timeoutsexpect that any callback could fail and may not be calledanticipate conditions where both inbound or outbound connections may hanguse Mikeal Roger’s ‘request’ moduleI contributed timeout functionalityshould be part of node core ~v0.7/0.8
offload anything computationally intensive
spawn a child processrequire('child_process').spawn
call out to another system more apt to handle heavy liftinguse a job queue
be specific with package dependencies
{ "name": "Foo Package", "description": "my Foo package", "version": "1.0.0", "author": "Mike Brevoort <[email protected]>", "dependencies": { "express": "2.3.2", "cluster": ">= 0.6.1", "mongodb": "0.9.x", "connect-gzip": "~0.1", "underscore": "= latest" }, "engines": { "node": "= 0.4.8" }}
is this what you really want? really?>
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@mbrevoortmike [at] brevoort [dot] com