Writing robust Node.js applications
Upcoming SlideShare
Loading in...5
×
 

Writing robust Node.js applications

on

  • 20,820 views

 

Statistics

Views

Total Views
20,820
Views on SlideShare
17,062
Embed Views
3,758

Actions

Likes
46
Downloads
436
Comments
13

13 Embeds 3,758

http://www.scoop.it 3500
https://twitter.com 162
http://lanyrd.com 51
http://www.linkedin.com 11
http://webcache.googleusercontent.com 9
http://us-w1.rockmelt.com 7
https://www.linkedin.com 6
http://eventifier.co 4
http://translate.googleusercontent.com 2
http://127.0.0.1 2
https://sendtoinc.com 2
http://photos1.servepics.com 1
http://twitter.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

CC Attribution-ShareAlike LicenseCC Attribution-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

15 of 13 Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • \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 Writing robust Node.js applications Presentation Transcript

  • Node.js TutorialTom Hughes-Croucher, PrincipalJetpacks For Dinosaurs, LLCtom@jetpacksfordinosaurs.com@sh1mmer
  • 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"
  • 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
  • Training Online In person
  • Node refresher
  • Code!
  • Interactive Debugging
  • Enki:~/Code/node-examples $ node --debughelloworld.jsdebugger listening on port 5858Server running at http://127.0.0.1:8124/
  • enki:~ sh1mmer$ ps -A | grep node19093 ttys001 0:00.39 node index.js19273 ttys002 0:00.00 grep nodeenki:~ sh1mmer$ kill -s USR1 19093
  • enki:~ sh1mmer$ node index.jsHit SIGUSR1 - starting debugger agent.debugger listening on port 5858
  • 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
  • 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
  • Express.js
  • Sinatra Style MVC framework
  • var app = require(express).createServer();app.get(/, function(req, res){ res.send(hello world);});app.listen(3000);
  • HTTP Verb Oriented
  • Middleware
  • 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);});
  • Templating
  • 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" ] } }});
  • 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
  • Building a chat server
  • Node Architectures
  • The obviousarchitecture
  • Client Front-end DatabaseUser Node DB
  • What about this?
  • Heavy Front-endClient Processor Database Farm WorkUser Node Node Node Node DB Node Node Node Node
  • Don’t we want to move “heavy work”?
  • Client → Server Server → DB Computation Computation
  • Better architecture
  • Front-endClient Database Farm DBUser Node Node Node Node Big Slow Logging farm Disk Node Node Disk
  • Move work which isn’tuser-facing out of main event loop
  • Ensure consistency of Node processes
  • Distributing work
  • Cluster
  • 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);}
  • Use load balancers to distribute across servers
  • node-proxy squid varnish haproxy
  • Unbundle SSL stud, stunnel, etchttp://vincent.bernat.im/en/blog/2011- ssl-benchmark.html#conclusion
  • Sharding
  • Disclaimer: Sharding is hardSee CAP theorem
  • Socket.io
  • Web Socket library
  • client and serverwith the shared API
  • 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);  });});
  • <!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>
  • Transports• WebSocket• Adobe® Flash® Socket• AJAX long polling• AJAX multipart streaming• Forever iframe• JSONP Polling
  • options = { transports: [websocket], log: util.log }io.listen(server, options)
  • // 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);  });});
  • 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
  • Using Redis with Socket.io
  • Why use Redis?• No shared memory• Cluster on server won’t share client data• Multiple servers can use the same Redis
  • 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); })});
  • 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); }) });}
  • Node in production
  • Error handling
  • 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});
  • 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)
  • 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) {
  • 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});
  • // Suppress errorsprocess.on(uncaughtException, function (err) { // Log it! console.dir(err);});
  • EventEmitter leaks
  • (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)
  • 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.
  • once() is awesome
  • 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");});
  • Process monitoring
  • Upstart
  • 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
  • forever
  • $ 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
  • NPM Packaging
  • Enki:~/Code/node-dnsserve(master) $ lsLICENSE README dnsserver.jspackage.jsonEnki:~/Code/node-dnsserve(master) $
  • { "name": "dns-server", "description": "DNS server for Node", "version": "0.0.1a", "author": "Tom Hughes-Croucher<tom.croucher@gmail.com>", "main" : "dnsserver"}
  • Enki:~/Code/node-dnsserve(master) $ sudo npminstall . -gdns-server@0.0.1a /usr/local/lib/node_modules/dns-serverEnki:~/Code/node-dnsserve(master) $
  • 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) $
  • Enki:~/Code/node-dnsserve(master) $ npm adduserUsername: foobarbazPassword:Email:
  • Enki:~/Code/node-dnsserve(master) $ npm publishnpm WARN Sending authorization over insecurechannel.Enki:~/Code/node-dnsserve(master) $
  • { "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" }}
  • { "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" }}
  • Questionstom@jetpacksfordinosaurs.com @sh1mmer