Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Node.js Async for the Rest of Us

Node.js Async for the Rest of Us

As presented at the Denver Open Source Users Group August 2011

Mike Brevoort

March 06, 2012
Tweet

More Decks by Mike Brevoort

Other Decks in Programming

Transcript

  1. node.js asynchronous... for the rest of us Mike Brevoort code

    sample can be found at https://github.com/mbrevoort/node.js-presentation 1
  2. agenda the case for node.js developing with node look at

    a few popular modules lessons from the trenches 2
  3. 3

  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 4
  5. typical i/o latency L1: 3 cycles L2: 14 cycles RAM:

    250 cycles DISK: 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 5
  6. “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/ 7
  7. 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 10
  8. 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! 10
  9. 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 11
  10. Installing node mac, linux or windows (with cygwin hell) fear

    not! stable windows support coming in v0.6.0 (any day)... BUT # 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 12
  11. Running Node REPL Read-Eval-Print-Loop Invoke script node 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+z 6 > require('fs').statSync('./blocking.js') { dev: 234881026, ino: 3162208, mode: 33188, nlink: 1, ..... 13
  12. 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/'); 14
  13. Not just HTTP var 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/ 15
  14. Developing with Node install node install npm your favorite text

    editor Sublime Text 2, Textmate, vim, Emacs, Eclipse, whatever 17
  15. npm publish, install, discover, and develop node programs puts modules

    in a place where node can find them manages dependencies 19
  16. npm search, install # install a module (copy to node_modules)

    npm install socket.io # install a specific version npm install [email protected] # install module globally npm install socket.io -g # search npm search socket #info npm info socket.io 20
  17. 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" } } 23
  18. 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 . 24
  19. Public NPM Private NPM NPM Proxy 1 2 GETs publish

    search back at the ranch... 26
  20. Public NPM Private NPM NPM Proxy 1 2 GETs publish

    search search back at the ranch... 26
  21. Public NPM Private NPM NPM Proxy 1 2 GETs publish

    search search http://npm.petdev.com:9000 back at the ranch... 26
  22. Public NPM Private NPM NPM Proxy 1 2 GETs publish

    search search http://npm.petdev.com:9000 http://npm.petdev.com:5984 back at the ranch... 26
  23. a very strong community nodejs.org Google Group mailing list IRC

    #node.js on freenode Stack Overflow, LinkedIn groups nodeconf, node summercamp, etc. 27
  24. 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
  25. 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
  26. 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 == :( 31
  27. Express request routing content negotiation view templating and partials session

    support static file serving fast, clean and powerful 35
  28. 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. 40
  29. 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 43
  30. 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 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!"); }); prudent approach nuclear approach > > 44
  31. 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') 45
  32. 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 46
  33. 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! 48
  34. 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
  35. 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
  36. 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
  37. 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 52
  38. 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
  39. 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 54
  40. 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
  41. 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? > 56