KEMBAR78
Building and Scaling Node.js Applications | PPTX
Building and Scaling
Node.js Applications
    Ohad Kravchick
      7/5/2012



    http://bit.ly/nodeApps
When to use Node and when not to
• Don't use node to server static files - there are
  better and easier [to configure] servers than node
  (NGINX, varnish, HAProxy)
• Don't use node for simple CRUD apps - you will
  have to build it all yourself
• You have to build almost everything yourself -
  great for APIs
   – Great for iterative development
• Use it for real-time
   – Node is responding to requests super fast
What do you need to start
• Node (http://nodejs.org/)
  – brew install node or download via URL
• nvm (https://github.com/creationix/nvm/)
  – nvm install v0.7.1
• npm (http://npmjs.org/)
  – we have our NYT repo
• online docs and tutorials, READMEs
• your favorite text editor
• node whatever.js
  – node compiles your code before every invocation
Simple web servers
• http
         1 var http = require('http');
         2 http.createServer(function (req, res) {
         3 res.writeHead(200, {'Content-Type': 'text/plain'});
         4 res.end('Hello Worldn');
         5 }).listen(1337, "127.0.0.1");
         6 console.log('Server running at http://127.0.0.1:1337/');


• express
         1 var express = require('express');
         2 var app = express.createServer();
         3 app.get('/', function(req, res){
         4 res.send('Hello World');
         5 });
         6 app.listen(3000);
Simple web servers
• Express supports:
  – HTTPS
  – Template-based views and partials + caching
  – Parameterized Routing /users/:user
  – Session
  – JSONP, MethodOverride, BodyParser, ENVs
  – Customizable Middleware
Middleware
 1 function loadUser(req, res, next) {
 2 // You would fetch your user from the db
 3 var user = users[req.params.id];
 4 if (user) {
 5 req.user = user;
 6 next();
 7 } else {
 8 next(new Error('Failed to load user ' + req.params.id));
 9 }
10 }
11
12 app.get('/user/:id', loadUser, function(req, res){
13 res.send('Viewing user ' + req.user.name);
14 });
Middleware plugins
• Uses connect
  1 var express = require('express');
  2 var app = express.createServer();
  3
  4 app.use(express.logger('dev'));
  5 app.use(express.bodyParser());
  6 app.use(express.cookieParser());
  7 app.use(express.favicon());
  8 app.use(express.session({ secret: 'my secret here' }));
  9 app.use(app.router);
 10 app.use(express.static('public'));
 11 app.use(express.directory('public'));
 12 app.use(express.errorHandler({showStack: true, dumpExceptions: true}));
 13
 14 app.get('/', function (req, res) {
 15 res.send('hello world');
 16 });
 17
 18 app.listen(3000);
Middleware plugins
•   logger request logger with custom format support
•   csrf Cross-site request forgery protection
•   compress Gzip compression middleware
•   basicAuth basic http authentication
•   bodyParser extensible request body parser
•   json application/json parser
•   urlencoded application/x-www-form-urlencoded parser
•   multipart multipart/form-data parser
•   cookieParser cookie parser
•   session session management support with bundled MemoryStore
•   cookieSession cookie-based session support
•   methodOverride faux HTTP method support
•   responseTime calculates response-time and exposes via X-Response-Time
•   staticCache memory cache layer for the static() middleware
•   static streaming static file server supporting Range and more
•   directory directory listing middleware
•   vhost virtual host sub-domain mapping middleware
•   favicon efficient favicon server (with default icon)
•   limit limit the bytesize of request bodies
•   query automatic querystring parser, populating req.query
•   errorHandler flexible error handler
Real-time using Node
• Socket.IO
   – WebSockets, Flash, long-polling, iframe forever
   – Supports IE 5.5+
   – Runs cross-domain
• Server      1 var io = require('socket.io').listen(80);
              2
              3 io.sockets.on('connection', function (socket) {
              4 socket.emit('news', { hello: 'world' });
              5 socket.on('my other event', function (data) {
              6 console.log(data);
              7 });
              8 });
              1 <script src="/socket.io/socket.io.js"></script>
• Client      2 <script>
              3 var socket = io.connect('http://localhost');
              4 socket.on('news', function (data) {
              5 console.log(data);
              6 socket.emit('my other event', { my: 'data' });
              7 });
              8 </script>
Simple DBs
• mysql
  1 var mysql = require('mysql');
  2 var connection = mysql.createConnection({host: 'example.org', user: 'bob', password: 'secret'});
  3 var state = 'NY';
  4 connection.query('SELECT * FROM users WHERE state = ?', [state], function(err, results) {
  5 var index;
  6 if (err) throw err;
  7 for (index in results) {
  8      console.log('Found user ' + fields[index].username + ' from ' + state);
  9 }
 10 connection.end(function(err) { });
 11 });




• mongo
  1 var mongodb = require('mongodb');
  2 var server = new mongodb.Server("127.0.0.1", 27017, {});
  3 new mongodb.Db('test', server, {}).open(function (error, client) {
  4 if (error) throw error;
  5 var collection = new mongodb.Collection(client, 'test_collection');
  6 collection.find({}, {limit:10}).toArray(function(err, docs) {
  7 console.dir(docs);
  8 });
  9 });
Debugging
• Node --debug whatever.js
• node-inspector
--------------------------------------------------------------          8080          ----------------
| Node.js server <--> 5858 <--> node-inspector |                 <-----------------> | web client |
--------------------------------------------------------------                        ----------------
                       DebuggerProtocol


      – Demo


• kill -s USR1 <pid>
• node --debug-brk much_faster_whatever.js
General
• Uncaught Exception kills the server
  – Log errors using process.on('uncaugtException')
  – deamontools supervise, monit
• Logging
  – Winston (https://github.com/flatiron/winston/)
• Use ab for testing
  – sudo sysctl -w net.inet.tcp.msl=300
  – Demo (ex3)
Concurrency model
• Non-blocking IO
   – Uses epoll, kqueue, select
• Single-threaded event-loop (uses libev and libeio)
   – As oppose to thread per request
   – less context-switching, less CPU usage, less memory
• The event loop is not intended to make
  computationally intensive tasks responsive, it’s meant
  to cut the resource waste related to waiting for I/O
  by Mikito Takada
• Long synchronous code WILL BLOCK ALL OTHERS
   – Extract heavy code to its own service or process
   – Use WebWorkers to spawn a “process”
Node vs. Apache
Maximizing the server
• Single thread running on a single CPU
• Use cluster to take all CPUs
  1 var cluster = require('cluster');
  2 var http = require('http');
  3 var numCPUs = require('os').cpus().length;
  4
  5 if (cluster.isMaster) {
  6 // Fork workers.
  7 for (var i = 0; i < numCPUs; i++) {
  8 cluster.fork();
  9 }
 10 } else {
 11 // Workers can share any TCP connection
 12 // In this case its a HTTP server
 13 http.createServer(function(req, res) {
 14 res.writeHead(200);
 15 res.end("hello worldn");
 16 }).listen(8000);
 17 }
Sharing between instances
• As you spawn multiple instances, you need to
  share data (=memory)
• You can use IPC
• You can use DB
• Or, you can use in-memory cache
  – memcached
     • Strings, integers, anything else is serialized
     • Set, get, delete, replace, append, prepend
     • Expiry
  – redis
redis
• Key-value storage that atomically supports:
   – Strings: get, set, strlen, append, getrange, setrange
   – Bit strings: setbit, getbit, bitcount, bitop
   – Integers and floats: incr, decr, incrby, decrby,
   – Lists: llen, lpop, lpush, lrem, linsert, lindex, lset, lrange,
   – Sets: sadd, sismember, srem, spop, smembers, scard
         • Inter-sets: sinter, sinterstore, sdiff, sdiffstore, sunion, sunionstore
   – Sorted sets: zscore, zadd, zrem, zrank, zrange, zrangebyscore
   – Hashes: hset, hget, hmset, hmget, hdel, hexists, hkeys, hvals, hgetall,
      hincrby
• Native to developers
• Makes the developer think about the implications of large-scale
• Demo
redis
 1 var redis = require("redis"),
 2       client = redis.createClient();
 3
 4 client.set("string key", "string val", redis.print);
 5 client.hset("hash key", "hashtest 1", "some value", redis.print);
 6 client.hset(["hash key", "hashtest 2", "some other value"], redis.print);
 7 client.hkeys("hash key", function (err, replies) {
 8 console.log(replies.length + " replies:");
 9 replies.forEach(function (reply, i) {
10        console.log(" " + i + ": " + reply);
11 });
12 client.quit();
13 });
redis
• Blazingly fast, in-memory cache
   – Size limited to memory
   – Good for ephemeral data (cache, session), for
     syncing servers
• Can be saved to disk, mostly for faster start up
• Use async/Q for handling multiple requests
• Supports expiry, pub-sub, transactions, server-
  side LUA scripts
• connect-redis for automatic session sync
Fin


Thank You



Questions?

Building and Scaling Node.js Applications

  • 1.
    Building and Scaling Node.jsApplications Ohad Kravchick 7/5/2012 http://bit.ly/nodeApps
  • 2.
    When to useNode and when not to • Don't use node to server static files - there are better and easier [to configure] servers than node (NGINX, varnish, HAProxy) • Don't use node for simple CRUD apps - you will have to build it all yourself • You have to build almost everything yourself - great for APIs – Great for iterative development • Use it for real-time – Node is responding to requests super fast
  • 3.
    What do youneed to start • Node (http://nodejs.org/) – brew install node or download via URL • nvm (https://github.com/creationix/nvm/) – nvm install v0.7.1 • npm (http://npmjs.org/) – we have our NYT repo • online docs and tutorials, READMEs • your favorite text editor • node whatever.js – node compiles your code before every invocation
  • 4.
    Simple web servers •http 1 var http = require('http'); 2 http.createServer(function (req, res) { 3 res.writeHead(200, {'Content-Type': 'text/plain'}); 4 res.end('Hello Worldn'); 5 }).listen(1337, "127.0.0.1"); 6 console.log('Server running at http://127.0.0.1:1337/'); • express 1 var express = require('express'); 2 var app = express.createServer(); 3 app.get('/', function(req, res){ 4 res.send('Hello World'); 5 }); 6 app.listen(3000);
  • 5.
    Simple web servers •Express supports: – HTTPS – Template-based views and partials + caching – Parameterized Routing /users/:user – Session – JSONP, MethodOverride, BodyParser, ENVs – Customizable Middleware
  • 6.
    Middleware 1 functionloadUser(req, res, next) { 2 // You would fetch your user from the db 3 var user = users[req.params.id]; 4 if (user) { 5 req.user = user; 6 next(); 7 } else { 8 next(new Error('Failed to load user ' + req.params.id)); 9 } 10 } 11 12 app.get('/user/:id', loadUser, function(req, res){ 13 res.send('Viewing user ' + req.user.name); 14 });
  • 7.
    Middleware plugins • Usesconnect 1 var express = require('express'); 2 var app = express.createServer(); 3 4 app.use(express.logger('dev')); 5 app.use(express.bodyParser()); 6 app.use(express.cookieParser()); 7 app.use(express.favicon()); 8 app.use(express.session({ secret: 'my secret here' })); 9 app.use(app.router); 10 app.use(express.static('public')); 11 app.use(express.directory('public')); 12 app.use(express.errorHandler({showStack: true, dumpExceptions: true})); 13 14 app.get('/', function (req, res) { 15 res.send('hello world'); 16 }); 17 18 app.listen(3000);
  • 8.
    Middleware plugins • logger request logger with custom format support • csrf Cross-site request forgery protection • compress Gzip compression middleware • basicAuth basic http authentication • bodyParser extensible request body parser • json application/json parser • urlencoded application/x-www-form-urlencoded parser • multipart multipart/form-data parser • cookieParser cookie parser • session session management support with bundled MemoryStore • cookieSession cookie-based session support • methodOverride faux HTTP method support • responseTime calculates response-time and exposes via X-Response-Time • staticCache memory cache layer for the static() middleware • static streaming static file server supporting Range and more • directory directory listing middleware • vhost virtual host sub-domain mapping middleware • favicon efficient favicon server (with default icon) • limit limit the bytesize of request bodies • query automatic querystring parser, populating req.query • errorHandler flexible error handler
  • 9.
    Real-time using Node •Socket.IO – WebSockets, Flash, long-polling, iframe forever – Supports IE 5.5+ – Runs cross-domain • Server 1 var io = require('socket.io').listen(80); 2 3 io.sockets.on('connection', function (socket) { 4 socket.emit('news', { hello: 'world' }); 5 socket.on('my other event', function (data) { 6 console.log(data); 7 }); 8 }); 1 <script src="/socket.io/socket.io.js"></script> • Client 2 <script> 3 var socket = io.connect('http://localhost'); 4 socket.on('news', function (data) { 5 console.log(data); 6 socket.emit('my other event', { my: 'data' }); 7 }); 8 </script>
  • 10.
    Simple DBs • mysql 1 var mysql = require('mysql'); 2 var connection = mysql.createConnection({host: 'example.org', user: 'bob', password: 'secret'}); 3 var state = 'NY'; 4 connection.query('SELECT * FROM users WHERE state = ?', [state], function(err, results) { 5 var index; 6 if (err) throw err; 7 for (index in results) { 8 console.log('Found user ' + fields[index].username + ' from ' + state); 9 } 10 connection.end(function(err) { }); 11 }); • mongo 1 var mongodb = require('mongodb'); 2 var server = new mongodb.Server("127.0.0.1", 27017, {}); 3 new mongodb.Db('test', server, {}).open(function (error, client) { 4 if (error) throw error; 5 var collection = new mongodb.Collection(client, 'test_collection'); 6 collection.find({}, {limit:10}).toArray(function(err, docs) { 7 console.dir(docs); 8 }); 9 });
  • 11.
    Debugging • Node --debugwhatever.js • node-inspector -------------------------------------------------------------- 8080 ---------------- | Node.js server <--> 5858 <--> node-inspector | <-----------------> | web client | -------------------------------------------------------------- ---------------- DebuggerProtocol – Demo • kill -s USR1 <pid> • node --debug-brk much_faster_whatever.js
  • 12.
    General • Uncaught Exceptionkills the server – Log errors using process.on('uncaugtException') – deamontools supervise, monit • Logging – Winston (https://github.com/flatiron/winston/) • Use ab for testing – sudo sysctl -w net.inet.tcp.msl=300 – Demo (ex3)
  • 13.
    Concurrency model • Non-blockingIO – Uses epoll, kqueue, select • Single-threaded event-loop (uses libev and libeio) – As oppose to thread per request – less context-switching, less CPU usage, less memory • The event loop is not intended to make computationally intensive tasks responsive, it’s meant to cut the resource waste related to waiting for I/O by Mikito Takada • Long synchronous code WILL BLOCK ALL OTHERS – Extract heavy code to its own service or process – Use WebWorkers to spawn a “process”
  • 14.
  • 15.
    Maximizing the server •Single thread running on a single CPU • Use cluster to take all CPUs 1 var cluster = require('cluster'); 2 var http = require('http'); 3 var numCPUs = require('os').cpus().length; 4 5 if (cluster.isMaster) { 6 // Fork workers. 7 for (var i = 0; i < numCPUs; i++) { 8 cluster.fork(); 9 } 10 } else { 11 // Workers can share any TCP connection 12 // In this case its a HTTP server 13 http.createServer(function(req, res) { 14 res.writeHead(200); 15 res.end("hello worldn"); 16 }).listen(8000); 17 }
  • 16.
    Sharing between instances •As you spawn multiple instances, you need to share data (=memory) • You can use IPC • You can use DB • Or, you can use in-memory cache – memcached • Strings, integers, anything else is serialized • Set, get, delete, replace, append, prepend • Expiry – redis
  • 17.
    redis • Key-value storagethat atomically supports: – Strings: get, set, strlen, append, getrange, setrange – Bit strings: setbit, getbit, bitcount, bitop – Integers and floats: incr, decr, incrby, decrby, – Lists: llen, lpop, lpush, lrem, linsert, lindex, lset, lrange, – Sets: sadd, sismember, srem, spop, smembers, scard • Inter-sets: sinter, sinterstore, sdiff, sdiffstore, sunion, sunionstore – Sorted sets: zscore, zadd, zrem, zrank, zrange, zrangebyscore – Hashes: hset, hget, hmset, hmget, hdel, hexists, hkeys, hvals, hgetall, hincrby • Native to developers • Makes the developer think about the implications of large-scale • Demo
  • 18.
    redis 1 varredis = require("redis"), 2 client = redis.createClient(); 3 4 client.set("string key", "string val", redis.print); 5 client.hset("hash key", "hashtest 1", "some value", redis.print); 6 client.hset(["hash key", "hashtest 2", "some other value"], redis.print); 7 client.hkeys("hash key", function (err, replies) { 8 console.log(replies.length + " replies:"); 9 replies.forEach(function (reply, i) { 10 console.log(" " + i + ": " + reply); 11 }); 12 client.quit(); 13 });
  • 19.
    redis • Blazingly fast,in-memory cache – Size limited to memory – Good for ephemeral data (cache, session), for syncing servers • Can be saved to disk, mostly for faster start up • Use async/Q for handling multiple requests • Supports expiry, pub-sub, transactions, server- side LUA scripts • connect-redis for automatic session sync
  • 20.