Writing robust Node.js applications

24,845 views

Published on

Published in: Technology
13 Comments
57 Likes
Statistics
Notes
No Downloads
Views
Total views
24,845
On SlideShare
0
From Embeds
0
Number of Embeds
3,746
Actions
Shares
0
Downloads
619
Comments
13
Likes
57
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Writing robust Node.js applications

    1. 1. Node.js TutorialTom Hughes-Croucher, PrincipalJetpacks For Dinosaurs, LLCtom@jetpacksfordinosaurs.com@sh1mmer
    2. 2. 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"
    3. 3. Scalable Server-Side Code with JavaScript Node Up and Running Tom Hughes-Croucher http://ofps.oreilly.com/titles/9781449398583/http://shop.oreilly.com/product/0636920015956.do
    4. 4. Training Online In person
    5. 5. Node refresher
    6. 6. Code!
    7. 7. Interactive Debugging
    8. 8. Enki:~/Code/node-examples $ node --debughelloworld.jsdebugger listening on port 5858Server running at http://127.0.0.1:8124/
    9. 9. enki:~ sh1mmer$ ps -A | grep node19093 ttys001 0:00.39 node index.js19273 ttys002 0:00.00 grep nodeenki:~ sh1mmer$ kill -s USR1 19093
    10. 10. enki:~ sh1mmer$ node index.jsHit SIGUSR1 - starting debugger agent.debugger listening on port 5858
    11. 11. Enki:~ $ sudo npm install -g node-inspectornode-inspector@0.1.8 ./node_modules/node-inspector├── websocket-server@1.4.04└── paperboy@0.0.2Enki:~ $ node-inspectorvisit http://0.0.0.0:8080/debug?port=5858 to startdebugging
    12. 12. Exercises• Modify a basic HTTP server to return the text "Im 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
    13. 13. Express.js
    14. 14. Sinatra Style MVC framework
    15. 15. var app = require(express).createServer();app.get(/, function(req, res){ res.send(hello world);});app.listen(3000);
    16. 16. HTTP Verb Oriented
    17. 17. Middleware
    18. 18. 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);});
    19. 19. Templating
    20. 20. 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" ] } }});
    21. 21. Exercise• 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
    22. 22. Building a chat server
    23. 23. Node Architectures
    24. 24. The obviousarchitecture
    25. 25. Client Front-end DatabaseUser Node DB
    26. 26. What about this?
    27. 27. Heavy Front-endClient Processor Database Farm WorkUser Node Node Node Node DB Node Node Node Node
    28. 28. Don’t we want to move “heavy work”?
    29. 29. Client → Server Server → DB Computation Computation
    30. 30. Better architecture
    31. 31. Front-endClient Database Farm DBUser Node Node Node Node Big Slow Logging farm Disk Node Node Disk
    32. 32. Move work which isn’tuser-facing out of main event loop
    33. 33. Ensure consistency of Node processes
    34. 34. Distributing work
    35. 35. Cluster
    36. 36. 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 worldn"); }).listen(8000);}
    37. 37. Use load balancers to distribute across servers
    38. 38. node-proxy squid varnish haproxy
    39. 39. Unbundle SSL stud, stunnel, etchttp://vincent.bernat.im/en/blog/2011- ssl-benchmark.html#conclusion
    40. 40. Sharding
    41. 41. Disclaimer: Sharding is hardSee CAP theorem
    42. 42. Socket.io
    43. 43. Web Socket library
    44. 44. client and serverwith the shared API
    45. 45. 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);  });});
    46. 46. <!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>
    47. 47. Transports• WebSocket• Adobe® Flash® Socket• AJAX long polling• AJAX multipart streaming• Forever iframe• JSONP Polling
    48. 48. options = { transports: [websocket], log: util.log }io.listen(server, options)
    49. 49. // note, io.listen() will create a http server for youvar 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);  });});
    50. 50. Exercises• 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
    51. 51. Using Redis with Socket.io
    52. 52. Why use Redis?• No shared memory• Cluster on server won’t share client data• Multiple servers can use the same Redis
    53. 53. 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); })});
    54. 54. 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); }) });}
    55. 55. Node in production
    56. 56. Error handling
    57. 57. 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});
    58. 58. 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) atArray.<anonymous> (module.js:423:10) at EventEmitter._tickCallback(node.js:126:26)
    59. 59. 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) {
    60. 60. 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});
    61. 61. // Suppress errorsprocess.on(uncaughtException, function (err) { // Log it! console.dir(err);});
    62. 62. EventEmitter leaks
    63. 63. (node) warning: possible EventEmitter memory leak detected. 11listeners 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)
    64. 64. 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, its done");});// Why are you using `.on()`?// You only expect this event to fire once.
    65. 65. once() is awesome
    66. 66. 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, its done");});
    67. 67. Process monitoring
    68. 68. Upstart
    69. 69. description "node.js server"author "croucher`"start on startupstop on shutdownscript # We found $HOME is needed. Without it, we ran intoproblems export HOME="/root" exec /usr/local/bin/node /var/noderoot/index.js 2>&1 >> /var/log/node.logend script
    70. 70. forever
    71. 71. $ cd /path/to/your/app$ forever start bin/yourappinfo: Running action: startinfo: Forever processingfile: examples/server.js$ forever listinfo: Running action: listinfo: Forever processes running [0] bin/yourapp [77041, 77040] /Users/You/.forever20dL.log 0:0:0:1.788
    72. 72. NPM Packaging
    73. 73. Enki:~/Code/node-dnsserve(master) $ lsLICENSE README dnsserver.jspackage.jsonEnki:~/Code/node-dnsserve(master) $
    74. 74. { "name": "dns-server", "description": "DNS server for Node", "version": "0.0.1a", "author": "Tom Hughes-Croucher<tom.croucher@gmail.com>", "main" : "dnsserver"}
    75. 75. Enki:~/Code/node-dnsserve(master) $ sudo npminstall . -gdns-server@0.0.1a /usr/local/lib/node_modules/dns-serverEnki:~/Code/node-dnsserve(master) $
    76. 76. Enki:~/Code/node-dnsserve(master) $ sudo npm link/usr/local/lib/node_modules/dns-server -> /Users/sh1mmer/Code/node-dnsserveEnki:~/Code/node-dnsserve(master) $ npm ls -g/usr/local/lib├── dns-server@0.0.1a -> /Users/sh1mmer/Code/node-dnsserve└─┬ npm@1.0.6 ├── abbrev@1.0.3 ├── node-uuid@1.1.0 ├── nopt@1.0.4 └── semver@1.0.5Enki:~/Code/node-dnsserve(master) $
    77. 77. Enki:~/Code/node-dnsserve(master) $ npm adduserUsername: foobarbazPassword:Email:
    78. 78. Enki:~/Code/node-dnsserve(master) $ npm publishnpm WARN Sending authorization over insecurechannel.Enki:~/Code/node-dnsserve(master) $
    79. 79. { "name": "winston", "description": "A multi-transport async logging library for Node.js", "version": "0.2.7", "author": "Charlie Robbins <charlie.robbins@gmail.com>", "contributors": [ { "name": "Matthew Bergman", "email": "mzbphoto@gmail.com" } ], "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" }}
    80. 80. { "name": "winston", "description": "A multi-transport async logging library for Node.js", "version": "0.2.7", "author": "Charlie Robbins <charlie.robbins@gmail.com>", "contributors": [ { "name": "Matthew Bergman", "email": "mzbphoto@gmail.com" } ], "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" }}
    81. 81. Questionstom@jetpacksfordinosaurs.com @sh1mmer

    ×