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

Writing robust Node.js applications

Writing robust Node.js applications

Felipe Coury

May 30, 2012
Tweet

More Decks by Felipe Coury

Other Decks in Programming

Transcript

  1. Introduction • Tom Hughes-Croucher • Principal at Jetpacks for Dinosaurs

    • Node.js and High-performance web site consulting and training • Walmart, MySpace, Joyent, Yahoo!, NASA, Tesco, etc • Node.js contributor • Lead author of "Node: Up and Running" Wednesday, May 30, 12
  2. Scalable Server-Side Code with JavaScript Tom Hughes-Croucher Node Up and

    Running http://ofps.oreilly.com/titles/9781449398583/ http://shop.oreilly.com/product/0636920015956.do Wednesday, May 30, 12
  3. Enki:~/Code/node-examples $ node --debug helloworld.js debugger listening on port 5858

    Server running at http://127.0.0.1:8124/ Wednesday, May 30, 12
  4. enki:~ sh1mmer$ ps -A | grep node 19093 ttys001 0:00.39

    node index.js 19273 ttys002 0:00.00 grep node enki:~ sh1mmer$ kill -s USR1 19093 Wednesday, May 30, 12
  5. enki:~ sh1mmer$ node index.js Hit SIGUSR1 - starting debugger agent.

    debugger listening on port 5858 Wednesday, May 30, 12
  6. Enki:~ $ sudo npm install -g node-inspector [email protected] ./node_modules/node-inspector ├──

    [email protected] └── [email protected] Enki:~ $ node-inspector visit http://0.0.0.0:8080/debug?port=5858 to start debugging Wednesday, May 30, 12
  7. Exercises • Modify a basic HTTP server to return the

    text "I'm learning Node" • Change the HTTP response to HTML and return your text in an HTML page • Return the User-Agent string from the browser as part of your HTML page Wednesday, May 30, 12
  8. app.use(express.bodyParser()); app.use(express.cookieParser()); app.post('/', function(req, res){ // Perhaps we posted several

    items with a form // (use the bodyParser() middleware for this) var items = req.body.items; console.log(items); res.send('logging'); }); Wednesday, May 30, 12
  9. var express = require("express"); app.configure(function () { var public =

    __dirname + "/../public/"; public = require("path").normalize(public); app.set("views", __dirname + "/views"); app.set("view engine", "jade"); }); app.get("/", function (req, res) { res.render("index", { locals : { h1: 'Router Stats', scripts : [ "/public/smoothie.js", "/public/raphael.js", "/public/base.js", "/public/gfx.js", "/public/explore.js", "/public/app.js" ] } } }); Wednesday, May 30, 12
  10. •Create an Express server •Make routes for ‘/’ & ‘/products’

    •Serve two different pages based on value of the query string parameter "page" •Create a redirect from /old to /new •Set a cookie on the client Exercise Wednesday, May 30, 12
  11. User Node DB Front-end Farm Database Client Node Node Node

    Node Node Node Node Heavy Processor Work Wednesday, May 30, 12
  12. User Node DB Front-end Farm Database Client Node Node Node

    Node Node Logging farm Disk Big Slow Disk Wednesday, May 30, 12
  13. var cluster = require('cluster'); var http = require('http'); var numCPUs

    = require('os').cpus().length; if (cluster.isMaster) { // Fork workers. for (var i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('death', function(worker) { console.log('worker ' + worker.pid + ' died'); }); } else { // Worker processes have a http server. http.Server(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000); } Wednesday, May 30, 12
  14. var app = require('express').createServer(), io = require('socket.io').listen(app); app.listen(80); app.get('/', function

    (req, res) { res.sendfile(__dirname + '/index.html'); }); io.sockets.on('connection', function (socket) { socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); }); Wednesday, May 30, 12
  15. <!DOCTYPE html> <html> <body> <script src="/socket.io/socket.io.js"></script> <script> var socket =

    io.connect('http://localhost'); socket.connect(); socket.on('connect', function(){ console.log('connect') }) socket.on('message', function(){ console.log('message') }) socket.on('disconnect', function() { console.log('disconnect'); }) </script> </body> </html> Wednesday, May 30, 12
  16. •WebSocket •Adobe® Flash® Socket •AJAX long polling •AJAX multipart streaming

    •Forever iframe •JSONP Polling Transports Wednesday, May 30, 12
  17. // note, io.listen() will create a http server for you

    var io = require('socket.io').listen(80); io.sockets.on('connection', function (socket) { io.sockets.emit('this', { will: 'be received by everyone'}); socket.on('private message', function (from, msg) { console.log('I received a private message by ', from, ' saying ', msg); }); socket.on('disconnect', function () { sockets.emit('user disconnected'); }); }); Wednesday, May 30, 12
  18. •Create a socket.io connect to an express server •Create a

    route which loads a page that includes socket.io •Send "hello world" to the client sockets •Make the client respond with "thanks" and disconnect from the server Exercises Wednesday, May 30, 12
  19. Why use Redis? • No shared memory • Cluster on

    server won’t share client data • Multiple servers can use the same Redis Wednesday, May 30, 12
  20. var express = require('express') , app = express.createServer(); var sio

    = require('socket.io') , RedisStore = sio.RedisStore , io = sio.listen(app); app.listen(8080); app.get('/', function(req,res) { res.sendfile(__dirname + '/index.html'); }); io.sockets.on('connection', function (socket) { socket.on('chat', function (data) { console.log(data); socket.broadcast.emit('chat', data); }) }); Wednesday, May 30, 12
  21. var cluster = require('cluster'); var http = require('http'); var numCPUs

    = require('os').cpus().length; if (cluster.isMaster) { // Fork workers. for (var i = 0; i < numCPUs; i++) { cluster.fork(); } } else { var express = require('express') , app = express.createServer(); var sio = require('socket.io') , RedisStore = sio.RedisStore , io = sio.listen(app); app.listen(8080); app.get('/', function(req,res) { res.sendfile(__dirname + '/index.html'); }); // Somehow pass this information to the workers io.set('store', new RedisStore); // Do the work here io.sockets.on('connection', function (socket) { socket.on('chat', function (data) { console.log(data); socket.broadcast.emit('chat', data); }) }); } Wednesday, May 30, 12
  22. var http = require('http'); var req = http.request({ host: 'www.google.com',

    path: '/', port: 80, method: 'POST' }, function (response) { // Do stuff with the response here }); Wednesday, May 30, 12
  23. node.js:134 throw e; // process.nextTick error, or 'error' event on

    first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js:47:15) at Object.<anonymous> (/ Users/you/y-u-no-listen-for-errors.js:5:9) at Module._compile (module.js:404:26) at Object..js (module.js:410:10) at Module.load (module.js:336:31) at Function._load (module.js:297:12) at Array.<anonymous> (module.js:423:10) at EventEmitter._tickCallback (node.js:126:26) Wednesday, May 30, 12
  24. var http = require('http'); try { var req = http.request({

    host: 'www.google.com', path: '/', port: 80, method: 'POST' }, function (response) { // Do stuff with the response here }); } catch(e) { //exception handling } Wednesday, May 30, 12
  25. var http = require('http'); var req = http.request({ host: 'www.google.com',

    path: '/', port: 80, method: 'POST' }, function (response) { // Do stuff with the response here }); req.on('error', function (err) { //safely handle this if possible }); Wednesday, May 30, 12
  26. (node) warning: possible EventEmitter memory leak detected. 11 listeners added.

    Use emitter.setMaxListeners() to increase limit. Trace: at Pool.<anonymous> (events.js:101:17) at Object.proxyRequest (~/http-proxy/lib/node-http-proxy.js:185:7) at Server.<anonymous> (/Users/some-user/myapp.js:14:9) at Server.emit (events.js:45:17) at HTTPParser.onIncoming (http.js:1078:12) at HTTPParser.onHeadersComplete (http.js:87:31) at Socket.ondata (http.js:977:22) at Socket._onReadable (net.js:654:27) at IOWatcher.onReadable [as callback] (net.js:156:10) Wednesday, May 30, 12
  27. var events = require('events'); function doSomethingThenTellMe () { var emitter

    = new events.EventEmitter(); setTimeout(function () { emitter.emit('done'); }, 2000); return emitter; } var doingIt = doSomethingThenTellMe(); doingIt.on('done', function () { console.log("Ok, it's done"); }); // Why are you using `.on()`? // You only expect this event to fire once. Wednesday, May 30, 12
  28. var events = require('events'); function doSomethingThenTellMe () { var emitter

    = new events.EventEmitter(); setTimeout(function () { emitter.emit('done'); }, 2000); return emitter; } var doingIt = doSomethingThenTellMe(); doingIt.once('done', function () { console.log("Ok, it's done"); }); Wednesday, May 30, 12
  29. description "node.js server" author "croucher`" start on startup stop on

    shutdown script # We found $HOME is needed. Without it, we ran into problems export HOME="/root" exec /usr/local/bin/node /var/noderoot/index.js 2>&1 >> /var/log/node.log end script Wednesday, May 30, 12
  30. $ cd /path/to/your/app $ forever start bin/yourapp info: Running action:

    start info: Forever processing file: examples/server.js $ forever list info: Running action: list info: Forever processes running [0] bin/yourapp [77041, 77040] /Users/You/.forever 20dL.log 0:0:0:1.788 Wednesday, May 30, 12
  31. { "name": "dns-server", "description": "DNS server for Node", "version": "0.0.1a",

    "author": "Tom Hughes-Croucher <[email protected]>", "main" : "dnsserver" } Wednesday, May 30, 12
  32. Enki:~/Code/node-dnsserve(master) $ sudo npm link /usr/local/lib/node_modules/dns-server -> /Users/ sh1mmer/Code/node-dnsserve Enki:~/Code/node-dnsserve(master)

    $ npm ls -g /usr/local/lib ├── [email protected] -> /Users/sh1mmer/Code/node- dnsserve └─┬ [email protected] ├── [email protected] ├── [email protected] ├── [email protected] └── [email protected] Enki:~/Code/node-dnsserve(master) $ Wednesday, May 30, 12
  33. Enki:~/Code/node-dnsserve(master) $ npm publish npm WARN Sending authorization over insecure

    channel. Enki:~/Code/node-dnsserve(master) $ Wednesday, May 30, 12
  34. { "name": "winston", "description": "A multi-transport async logging library for

    Node.js", "version": "0.2.7", "author": "Charlie Robbins <[email protected]>", "contributors": [ { "name": "Matthew Bergman", "email": "[email protected]" } ], "repository": { "type": "git", "url": "http://github.com/indexzero/winston.git" }, "keywords": ["logging", "sysadmin", "tools"], "dependencies": { "colors": ">= 0.3.0", "eyes": ">= 0.1.6", "loggly": ">= 0.1.4", "vows": ">= 0.5.2" }, "main": "./lib/winston", "scripts": { "test": "vows test/*-test.js --spec" }, "engines": { "node": ">= 0.3.0" } } Wednesday, May 30, 12
  35. { "name": "winston", "description": "A multi-transport async logging library for

    Node.js", "version": "0.2.7", "author": "Charlie Robbins <[email protected]>", "contributors": [ { "name": "Matthew Bergman", "email": "[email protected]" } ], "repository": { "type": "git", "url": "http://github.com/indexzero/winston.git" }, "keywords": ["logging", "sysadmin", "tools"], "dependencies": { "colors": ">= 0.x.x", "eyes": ">= 0.1.x", "loggly": ">= 0.1.x", "vows": ">= 0.5.x" }, "main": "./lib/winston", "scripts": { "test": "vows test/*-test.js --spec" }, "engines": { "node": ">= 0.3.0" } } Wednesday, May 30, 12