SlideShare ist ein Scribd-Unternehmen logo
1 von 29
Downloaden Sie, um offline zu lesen
REAL TIME WEB
                          WITH NODE
                           By Tim Caswell




Saturday, June 5, 2010
Live Interaction
            The web is about doing things, not just tourism anymore.

Saturday, June 5, 2010
Uses of Live Interaction
                         Chat Widget
                         Twitter Feed
                         Stock Ticker
                         Real-Time Game
                         Collaborative
                         Documents
                         TXJS Demos


Saturday, June 5, 2010
Why we need non-blocking
                  Polling is too slow and inefficient.
                  Live interaction requires the server to push data.
                  In order to push data, the connections need to be
                  persistent.
                  The server needs to handle thousands of persistent
                  connections
                  Threads aren’t going to scale.


Saturday, June 5, 2010
This is your server.
                         (with blocking I/O)
Saturday, June 5, 2010
This is your server
                  with low concurrency.




Saturday, June 5, 2010
This is your server
               with high concurrency.

Saturday, June 5, 2010
Connect
              We’ll use a new node
                framework that
            “connects” the web users
                 to each other.




Saturday, June 5, 2010
It’s like Japanese Legos
Saturday, June 5, 2010
Pre-Built Blocks
         Connect.createServer([
             {filter: "log"},
             {filter: "body-decoder", route: "/stream"},
             {provider: "pubsub", route: "/stream"},
             {filter: "conditional-get"},
             {filter: "cache"},
             {filter: "gzip"},
             {provider: "cache-manifest", root: root},
             {provider: "static", root: root}
         ]);



Saturday, June 5, 2010
And easy too!
Saturday, June 5, 2010
method-override.js
           var key;
           // Initialize any state (on server startup)
           exports.setup = function (env) {
               key = this.key || "_method";
           };
           // Modify the request stream (on request)
           exports.handle = function(err, req, res, next){
               if (key in req.body) {
                    req.method = req.body[key].toUpperCase();
               }
               next();
           };

Saturday, June 5, 2010
response-time.js
        exports.handle = function(err, req, res, next){
            var start = new Date,
                writeHead = res.writeHead;

                     res.writeHead = function(code, headers){
                         res.writeHead = writeHead;
                         headers['X-Response-Time'] =
                                        (new Date - start) + "ms";
                         res.writeHead(code, headers);
                     };

                     next();
        };

Saturday, June 5, 2010
Well, actually, it’s
             not always easy.
Saturday, June 5, 2010
static.js




Saturday, June 5, 2010
static.js
   var fs = require('fs'),
       Url = require('url'),
       Path = require('path');

   var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime

   var DEFAULT_MIME = 'application/octet-stream';

   module.exports = {

         setup: function (env) {
             this.root = this.root || process.cwd();
         },

         handle: function (err, req, res, next) {
             // Skip on error
             if (err) {
                 next();
                 return;
             }
             var url = Url.parse(req.url);

               var pathname = url.pathname.replace(/..+/g, '.'),
                   filename = Path.join(this.root, pathname);

               if (filename[filename.length - 1] === "/") {
                   filename += "index.html";
               }


Saturday, June 5, 2010
static.js
                                           // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'),                 var events = [];
       Url = require('url'),               function onData() {
       Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                             browser cache lifetime
                                                events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var DEFAULT_MIME = 'application/octet-stream';
                                           req.addListener("data", onData);
   module.exports = {                      req.addListener("end", onEnd);


         setup: function (env) {             fs.stat(filename, function (err, stat) {
             this.root = this.root || process.cwd();
         },                                      // Stop buffering events
                                                 req.removeListener("data", onData);
         handle: function (err, req, res, next) {req.removeListener("end", onEnd);
               // Skip on error
               if (err) {                          // Fall through for missing files, thow error for other problems
                   next();                         if (err) {
                   return;                             if (err.errno === process.ENOENT) {
               }                                           next();
               var url = Url.parse(req.url);               // Refire the buffered events
                                                           events.forEach(function (args) {
                                                               req.emit.apply(req, args);
               var pathname = url.pathname.replace(/..+/g, '.'),
                                                           });
                   filename = Path.join(this.root, pathname);
                                                           return;
               if (filename[filename.length - 1] === "/") {
                   filename += "index.html";
               }


Saturday, June 5, 2010
static.js
                                            // Buffer any events that fire while waiting on the stat.
    var fs = require('fs'),                 var events = [];
        Url = require('url'),               function onData() {
        Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                            }
    var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                              browser cache lifetime
                                                 events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                            }
    var DEFAULT_MIME = 'application/octet-stream';
                                            req.addListener("data", onData);
    module.exports = {                      req.addListener("end", onEnd);

(err);
        setup: function (env) {             fs.stat(filename, function (err, stat) {
rn;
            this.root = this.root || process.cwd();
        },                                      // Stop buffering events
                                                req.removeListener("data", onData);
                                                req.removeListener("end", onEnd);
 the file directly using (err, req, res, next) {
        handle: function buffers
ile(filename, function error data) {
            // Skip on (err,
            if (err) {                          // Fall through for missing files, thow error for other problems
err) {
                next();                         if (err) {
next(err);
                return;                             if (err.errno === process.ENOENT) {
return;
            }                                           next();
            var url = Url.parse(req.url);               // Refire the buffered events
writeHead(200, {
                                                        events.forEach(function (args) {
ontent-Type": Mime.type(filename),
                                                            req.emit.apply(req, args);
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'),
            var data.length,
                                                        });
                filename = Path.join(this.root, pathname);
ast-Modified": stat.mtime.toUTCString(),
                                                        return;
/ Cache in browser for 1 year
ache-Control": (filename[filename.length - 1] === "/") {
            if "public max-age=" + 31536000
                filename += "index.html";
end(data); }

 Saturday, June 5, 2010
static.js
                                           // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'),                 var events = [];
       Url = require('url'),               function onData() {
       Path = require('path');                  events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() {
                                             browser cache lifetime
                                       };       events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                           }
   var DEFAULT_MIME = 'application/octet-stream';
                                       // Mini mime module for static file serving
                                           req.addListener("data", onData);
   module.exports = {                  var req.addListener("end", onEnd);
                                            Mime = {

(err);                                          type: function getMime(path) (err, stat) {
          setup: function (env) {                 fs.stat(filename, function {
rn;                                                var index = path.lastIndexOf(".");
                this.root = this.root || process.cwd();
          },                                       if (index < buffering events
                                                       // Stop 0) {
                                                     return DEFAULT_MIME;
                                                       req.removeListener("data", onData);
                                                   } req.removeListener("end", onEnd);
 the file directly using (err, req, res, next) { type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
          handle: function buffers
                                                   var
ile(filename,   // function error data) {
                     Skip on (err,
                if (err) {                         return Fall through for missing files, thow error "; charset=utf-8" : type;
                                                       // (/(text|javascript)/).test(type) ? type + for other problems
err) {                                          },
                      next();                          if (err) {
next(err);
                      return;                              if (err.errno === process.ENOENT) {
return;                                         TYPES : { ".3gp"
                }                                              next(); "video/3gpp",
                                                                     :
                var url = Url.parse(req.url);               ".a" Refire the buffered events
                                                               //    : "application/octet-stream",
writeHead(200, {                                            ".ai"    : "application/postscript",
                                                               events.forEach(function (args) {
ontent-Type": Mime.type(filename),                          ".aif" req.emit.apply(req, args);
                                                                     : "audio/x-aiff",
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length,
                                                            ".aiff" :
                                                               });
                      filename = Path.join(this.root, pathname);
ast-Modified": stat.mtime.toUTCString(),                    ".asc"
                                                               return; "application/pgp-signature",
                                                                     :
/ Cache in browser for 1 year                               ".asf"   : "video/x-ms-asf",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                                                            ".asm"   : "text/x-asm",
                      filename += "index.html";
                                                            ".asx"   : "video/x-ms-asf",
end(data);      }
                                                            ".atom" : "application/atom+xml",
                                                            ".au"    : "audio/basic",
 Saturday, June 5, 2010                                     ".avi"   : "video/x-msvideo",
static.js
                                            // Buffer any events that fire while waiting on the stat.
   var fs = require('fs'), : "video/x-flv",var events = [];
                  ".flv"
                  ".for"   :                function onData() {
       Url = require('url'), "text/x-fortran",
       Path = require('path');
                  ".gem"                        events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                           : "application/octet-stream",
                                            }
                  ".gemspec" : "text/x-script.ruby",
   var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                  1000 *       60; // 1 hour browser cache lifetime
                                         };     events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                  ".gz"    : "application/x-gzip",
                  ".h"     : "text/x-c",    }
   var DEFAULT_MIME = 'application/octet-stream';
                                         // Mini mime module for static file serving
                                            req.addListener("data", onData);
                  ".hh"    : "text/x-c",
   module.exports ".htm"
                  = {                    var Mime = {
                           : "text/html", req.addListener("end", onEnd);
(err);                   ".html" : "text/html",
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"   : {            type: function
                                                  fs.stat(filename, function {
                                                    var index = path.lastIndexOf(".");
                this.root = this.root || process.cwd();
                         ".ics"   : "text/calendar",
          },             ".ifb"   : "text/calendar", (index < buffering events
                                                    if // Stop 0) {
                                                      return DEFAULT_MIME;
                                                       req.removeListener("data", onData);
                         ".iso"   : "application/octet-stream",
                                                    }
                                  : req, res, next) {req.removeListener("end", onEnd);
 the file directly ".jar" (err,"application/java-archive",
          handle: function buffers
                          using                     var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
ile(filename,   // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",
                if (err) {                          return Fall through for missing files, thow error "; charset=utf-8" : type;
                                                       // (/(text|javascript)/).test(type) ? type + for other problems
                         ".jnlp" : "application/x-java-jnlp-file",
err) {                                           },
                      next();
                         ".jpeg" : "image/jpeg",       if (err) {
next(err);
                      return;
                         ".jpg"   : "image/jpeg",           if (err.errno === process.ENOENT) {
return;                                          TYPES : { ".3gp"     : "video/3gpp",
                }        ".js"    : "application/javascript", next();
                var url = Url.parse(req.url);                ".a" Refire the buffered events
                                                                //    : "application/octet-stream",
writeHead(200, { ".json" : "application/json",               ".ai"    : "application/postscript",
                         ".log"   : "text/plain",               events.forEach(function (args) {
ontent-Type": Mime.type(filename),                           ".aif" req.emit.apply(req, args);
                                                                      : "audio/x-aiff",
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"   :
                                                             ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"   : "video/mp4",                });
ast-Modified": stat.mtime.toUTCString(),                     ".asc"
                                                                return; "application/pgp-signature",
                                                                      :
/ Cache in browser ".man" year "text/troff",
                          for 1   :
                                                             ".asf"   : "video/x-ms-asf",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                             ".asm"   : "text/x-asm",
                      filename += : "application/mbox",
                         ".mbox" "index.html";
                                                             ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"    : "text/troff",            ".atom" : "application/atom+xml",
                                                             ".au"    : "audio/basic",
                         ".mid"   : "audio/midi",
 Saturday, June 5, 2010                                      ".avi"   : "video/x-msvideo",
static.js
                                                   // Buffer any events that fire while waiting "text/x-c",
                                                                                       ".cc"    : on the stat.
    var fs = require('fs'), : "video/x-flv",var events = [];
                         ".flv"                                                        ".chm"   : "application/vnd.ms-htmlhelp",
          Url = require('url'), "text/x-fortran",
                         ".for"   :                function onData() {                 ".class"   : "application/octet-stream",
          Path = require('path');
                         ".gem"                         events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                  : "application/octet-stream",                        ".com"   : "application/x-msdownload",
                                                   }
                         ".gemspec" : "text/x-script.ruby",                            ".conf" : "text/plain",
    var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                         1000 *       60; // 1 hour browser cache lifetime             ".cpp"   : "text/x-c",
                                               };       events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                                                                       ".crt"   : "application/x-x509-ca-cert",
                         ".gz"    : "application/x-gzip",
    var DEFAULT_MIME = 'application/octet-stream';
                         ".h"     : "text/x-c",    }                                   ".css"   : "text/css",
                                               // Mini mime module for static file serving
                                                   req.addListener("data", onData); ".csv"      : "text/csv",
                         ".hh"    : "text/x-c",
    module.exports ".htm"= {                   var Mime = {
                                  : "text/html", req.addListener("end", onEnd);        ".cxx"   : "text/x-c",
                         ".html" : "text/html",                                        ".deb"   : "application/x-debian-package",
(err);
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"   : {             type: function
                                                   fs.stat(filename, function {        ".der"   : "application/x-x509-ca-cert",
                                                     var index = path.lastIndexOf(".");".diff" : "text/x-diff",
                this.root = this.root || process.cwd();
                         ".ics"   : "text/calendar",
          },             ".ifb"   : "text/calendar", (index < buffering events
                                                     if // Stop 0) {                   ".djv"   : "image/vnd.djvu",
                                                       return DEFAULT_MIME;
                                                        req.removeListener("data", onData);
                                                                                       ".djvu" : "image/vnd.djvu",
                         ".iso"   : "application/octet-stream",
                                                     }
                                  : req, res, next) {req.removeListener("end", onEnd); ".dll"   : "application/x-msdownload",
 the file directly ".jar" (err,"application/java-archive",
          handle: function buffers
                          using                      var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
                // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",                               ".dmg"   : "application/octet-stream",
ile(filename,                                        return Fall through for missing files, thow error "; charset=utf-8" : type;
                if (err) {                              // (/(text|javascript)/).test(type) ? : "application/msword",
                         ".jnlp" : "application/x-java-jnlp-file",                     ".doc"   type + for other problems
err) {                                            },
                      next();
                         ".jpeg" : "image/jpeg",        if (err) {                     ".dot"   : "application/msword",
next(err);
                      return;
                         ".jpg"   : "image/jpeg",            if (err.errno === process.ENOENT) { "application/xml-dtd",
                                                                                       ".dtd"   :
return;                                           TYPES : { ".3gp"     : "video/3gpp", ".dvi"
                }        ".js"    : "application/javascript", next();                           : "application/x-dvi",
                var url = Url.parse(req.url);                 ".a" Refire the buffered events : "application/java-archive",
                                                                 //    : "application/octet-stream",
                                                                                       ".ear"
writeHead(200, { ".json" : "application/json",                ".ai"    : "application/postscript",
                         ".log"   : "text/plain",                events.forEach(function (args) : "message/rfc822",
                                                                                       ".eml"   {
ontent-Type": Mime.type(filename),                            ".aif" req.emit.apply(req, args); : "application/postscript",
                                                                       : "audio/x-aiff",
                                                                                       ".eps"
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"   :
                                                              ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"   : "video/mp4",                 });                   ".exe"   : "application/x-msdownload",
ast-Modified": stat.mtime.toUTCString(),                      ".asc"
                                                                 return; "application/pgp-signature",
                                                                       :               ".f"     : "text/x-fortran",
/ Cache in browser ".man" year "text/troff",
                          for 1   :
                                                              ".asf"   : "video/x-ms-asf",
                                                                                       ".f77"   : "text/x-fortran",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                              ".asm"   : "text/x-asm", ".f90"
                      filename += : "application/mbox",
                         ".mbox" "index.html";                                                  : "text/x-fortran",
                                                              ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"    : "text/troff",             ".atom" : "application/atom+xml",
                                                              ".au"    : "audio/basic",
                         ".mid"   : "audio/midi",
 Saturday, June 5, 2010                                       ".avi"   : "video/x-msvideo",
static.js
                                                     // Buffer any events that fire while waiting "text/x-c",
                                                                                         ".cc"    : on the stat.
    var fs = require('fs'), : "video/x-flv",var events = [];
                         ".flv"                                                          ".chm"   : "application/vnd.ms-htmlhelp",
          Url = require('url'), "text/x-fortran",
                         ".for"    :                 function onData() {                 ".class"   : "application/octet-stream",
          Path = require('path');
                         ".gem"                            events.push(["data"].concat(Array.prototype.slice.call(arguments)));
                                   : "application/octet-stream",                         ".com"   : "application/x-msdownload",
                                                     }
                         ".gemspec" : "text/x-script.ruby",                              ".conf" : "text/plain",
    var lifetime = ".gif" 60: *"image/gif", function onEnd() {
                         1000 *        60; // 1 hour browser cache lifetime              ".cpp"   : "text/x-c",
                                                 };        events.push(["end"].concat(Array.prototype.slice.call(arguments)));
                                                                                         ".crt"   : "application/x-x509-ca-cert",
                         ".gz"     : "application/x-gzip",
    var DEFAULT_MIME = 'application/octet-stream';
                         ".h"      : "text/x-c",     }                                   ".css"   : "text/css",
                                                 // Mini mime module for static file serving
                                                     req.addListener("data", onData); ".csv"      : "text/csv",
                         ".hh"     : "text/x-c",
    module.exports ".htm"= {                     var Mime = {
                                   : "text/html", req.addListener("end", onEnd);         ".cxx"   : "text/x-c",
                         ".html" : "text/html",                                          ".deb"   : "application/x-debian-package",
(err);
rn;       setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) {
                         ".ico"    : {              type: function
                                                     fs.stat(filename, function {        ".der"   : "application/x-x509-ca-cert",
                                                       var index = path.lastIndexOf(".");".diff" : "text/x-diff",
                this.root = this.root || process.cwd();
                         ".ics"    : "text/calendar",
          },             ".ifb"    : "text/calendar", (index < buffering events
                                                       if // Stop 0) {                   ".djv"   : "image/vnd.djvu",
                                                         return DEFAULT_MIME;
                                                           req.removeListener("data", onData);
                                                                                         ".djvu" : "image/vnd.djvu",
                         ".iso"    : "application/octet-stream",
                                                       } req.removeListener("end", onEnd);
                                                                                         ".dll"   : "application/x-msdownload",
 the file directly ".jar"".bmp" req, "image/bmp", type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME;
          handle: function (err,"application/java-archive",
                          using buffers : res, next) {
                                   :
                                                       var
                // function error: data) {
                     Skip".java"
                          on (err, "text/x-java-source",
                                ".bz2"    : "application/x-bzip2",                       ".dmg"   : "application/octet-stream",
ile(filename,                                          return (/(text|javascript)/).test(type) ? : "application/msword", : type;
                                                                                                  type + "; charset=utf-8"
err) {          if (err) {                : "text/x-c", // Fall through for missing files, thow error for other problems
                         ".jnlp" : "application/x-java-jnlp-file",
                                ".c"                                                     ".doc"
                                                    },     if (err) {                    ".dot"   : "application/msword",
next(err);            next(); ".cab"
                         ".jpeg" : "image/jpeg",
                                          : "application/vnd.ms-cab-compressed",
                      return;
                         ".jpg"    : "image/jpeg",             if (err.errno === process.ENOENT) { "application/xml-dtd",
                                                                                         ".dtd"   :
return;                                             TYPES : { ".3gp"     : "video/3gpp", ".dvi"
                }        ".js"     : "application/javascript", next();                            : "application/x-dvi",
                var url = Url.parse(req.url);                   ".a" Refire the buffered events : "application/java-archive",
                                                                   //    : "application/octet-stream",
                                                                                         ".ear"
writeHead(200, { ".json" : "application/json",                  ".ai"    : "application/postscript",
                         ".log"    : "text/plain",                 events.forEach(function (args) : "message/rfc822",
                                                                                         ".eml"   {
ontent-Type": Mime.type(filename),                              ".aif" req.emit.apply(req, args); : "application/postscript",
                                                                         : "audio/x-aiff",
                                                                                         ".eps"
ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff",
                var data.length, "audio/x-mpegurl",
                         ".m3u"    :
                                                                ".aiff" :
                      filename = Path.join(this.root, pathname);
                         ".m4v"    : "video/mp4",                  });                   ".exe"   : "application/x-msdownload",
ast-Modified": stat.mtime.toUTCString(),                        ".asc"
                                                                   return; "application/pgp-signature",
                                                                         :               ".f"     : "text/x-fortran",
/ Cache in browser ".man" year "text/troff",
                          for 1    :
                                                                ".asf"   : "video/x-ms-asf",
                                                                                         ".f77"   : "text/x-fortran",
ache-Control": (filename[filename.length - 1] === "/") {
                if "public max-age=" + 31536000
                         ".mathml" : "application/mathml+xml",
                                                                ".asm"   : "text/x-asm", ".f90"
                      filename += : "application/mbox",
                         ".mbox" "index.html";                                                    : "text/x-fortran",
                                                                ".asx"   : "video/x-ms-asf",
end(data);      }        ".mdoc" : "text/troff",
                         ".me"     : "text/troff",              ".atom" : "application/atom+xml",
                                                                ".au"    : "audio/basic",
                         ".mid"    : "audio/midi",
 Saturday, June 5, 2010                                         ".avi"   : "video/x-msvideo",
Built-in Filter Modules
                   Authentication     Error Handler
                   Authorization      Gzip
                   Body Decoder       Log
                   Cache              Method Override
                   Conditional Get    Response Time
                   Debug              Session


Saturday, June 5, 2010
Built-in Data Providers

                         Static        Cache Manifest
                         Rest          Direct
                         Router        JSON-RPC
                         PubSub        More...


Saturday, June 5, 2010
Demo Time!


Saturday, June 5, 2010
app.js (stack)
                   require.paths.unshift("./lib");
                   var Connect = require('connect');
                   var root = __dirname + "/public";

                   module.exports = Connect.createServer([
                       {filter: "log"},
                       {filter: "body-decoder"},
                       {provider: "pubsub", route: "/stream",
                           logic: Backend},
                       {filter: "conditional-get"},
                       {filter: "cache"},
                       {filter: "gzip"},
                       {provider: "cache-manifest", root: root},
                       {provider: "static", root: root}
                   ]);

Saturday, June 5, 2010
app.js (Backend)
                         var Backend = {
                             subscribe: function (subscriber) {
                                 if (subscribers.indexOf(subscriber) < 0) {
                                     subscribers.push(subscriber);
                                 }
                             },
                             unsubscribe: function (subscriber) {
                                 var pos = subscribers.indexOf(subscriber);
                                 if (pos >= 0) {
                                     subscribers.slice(pos);
                                 }
                             },
                             publish: function (message, callback) {
                                 subscribers.forEach(function (subscriber) {
                                     subscriber.send(message);
                                 });
                                 callback();
                             }
                         };

Saturday, June 5, 2010
http://github.com/extjs/connect

             http://twitter.com/creationix

             http://raphaeljs.com

             http://nodejs.org


Saturday, June 5, 2010
Any
                         Questions
                            ?




Saturday, June 5, 2010

Weitere ähnliche Inhalte

Was ist angesagt?

Connectivity coding for java and mysql
Connectivity coding for java and mysqlConnectivity coding for java and mysql
Connectivity coding for java and mysqlFahad Ali Khan
 
Ejemplo radio
Ejemplo radioEjemplo radio
Ejemplo radiolupe ga
 
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)Simon Su
 
node.js Module Development
node.js Module Developmentnode.js Module Development
node.js Module DevelopmentJay Harris
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & ToolsIan Barber
 
The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212Mahmoud Samir Fayed
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 SpringKiyotaka Oku
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in Practiceericbeyeler
 
Serial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profitSerial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profitblaufish
 
Pry, the good parts
Pry, the good partsPry, the good parts
Pry, the good partsConrad Irwin
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Arian Gutierrez
 
Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)Boxed Ice
 
Introduce leo-redundant-manager
Introduce leo-redundant-managerIntroduce leo-redundant-manager
Introduce leo-redundant-managerParas Patel
 

Was ist angesagt? (19)

Connectivity coding for java and mysql
Connectivity coding for java and mysqlConnectivity coding for java and mysql
Connectivity coding for java and mysql
 
Ejemplo radio
Ejemplo radioEjemplo radio
Ejemplo radio
 
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)JCConf 2015  - 輕鬆學google的雲端開發 - Google App Engine入門(上)
JCConf 2015 - 輕鬆學google的雲端開發 - Google App Engine入門(上)
 
занятие8
занятие8занятие8
занятие8
 
Micro services workshop
Micro services workshopMicro services workshop
Micro services workshop
 
node.js Module Development
node.js Module Developmentnode.js Module Development
node.js Module Development
 
Intro to Redis
Intro to RedisIntro to Redis
Intro to Redis
 
Debugging: Rules & Tools
Debugging: Rules & ToolsDebugging: Rules & Tools
Debugging: Rules & Tools
 
Capistrano Rails
Capistrano RailsCapistrano Rails
Capistrano Rails
 
The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212The Ring programming language version 1.10 book - Part 92 of 212
The Ring programming language version 1.10 book - Part 92 of 212
 
JJUG CCC 2011 Spring
JJUG CCC 2011 SpringJJUG CCC 2011 Spring
JJUG CCC 2011 Spring
 
zinno
zinnozinno
zinno
 
Java Concurrency in Practice
Java Concurrency in PracticeJava Concurrency in Practice
Java Concurrency in Practice
 
Swing database(mysql)
Swing database(mysql)Swing database(mysql)
Swing database(mysql)
 
Serial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profitSerial Killers - or Deserialization for fun and profit
Serial Killers - or Deserialization for fun and profit
 
Pry, the good parts
Pry, the good partsPry, the good parts
Pry, the good parts
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
 
Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)Monitoring MongoDB (MongoSV)
Monitoring MongoDB (MongoSV)
 
Introduce leo-redundant-manager
Introduce leo-redundant-managerIntroduce leo-redundant-manager
Introduce leo-redundant-manager
 

Andere mochten auch

Node Powered Mobile
Node Powered MobileNode Powered Mobile
Node Powered MobileTim Caswell
 
Open design at large scale
Open design at large scaleOpen design at large scale
Open design at large scaleshykes
 
Perspectives on Docker
Perspectives on DockerPerspectives on Docker
Perspectives on DockerRightScale
 
Open Design at large scale by Solomon Hykes
Open Design at large scale by Solomon HykesOpen Design at large scale by Solomon Hykes
Open Design at large scale by Solomon HykesDocker, Inc.
 
Docker Deployments
Docker DeploymentsDocker Deployments
Docker DeploymentsDocker, Inc.
 
Why Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your ShellWhy Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your Shelljaguardesignstudio
 

Andere mochten auch (6)

Node Powered Mobile
Node Powered MobileNode Powered Mobile
Node Powered Mobile
 
Open design at large scale
Open design at large scaleOpen design at large scale
Open design at large scale
 
Perspectives on Docker
Perspectives on DockerPerspectives on Docker
Perspectives on Docker
 
Open Design at large scale by Solomon Hykes
Open Design at large scale by Solomon HykesOpen Design at large scale by Solomon Hykes
Open Design at large scale by Solomon Hykes
 
Docker Deployments
Docker DeploymentsDocker Deployments
Docker Deployments
 
Why Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your ShellWhy Zsh is Cooler than Your Shell
Why Zsh is Cooler than Your Shell
 

Ähnlich wie Real Time Web with Node

NodeJs
NodeJsNodeJs
NodeJsdizabl
 
JS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsJS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsTimur Shemsedinov
 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leetjohndaviddalton
 
node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascriptEldar Djafarov
 
Node.js - A practical introduction (v2)
Node.js  - A practical introduction (v2)Node.js  - A practical introduction (v2)
Node.js - A practical introduction (v2)Felix Geisendörfer
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.jsWebsecurify
 
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"GeeksLab Odessa
 
JavaScript - Like a Box of Chocolates
JavaScript - Like a Box of ChocolatesJavaScript - Like a Box of Chocolates
JavaScript - Like a Box of ChocolatesRobert Nyman
 
Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015Lukas Ruebbelke
 
Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010Ismael Celis
 
Jersey framework
Jersey frameworkJersey framework
Jersey frameworkknight1128
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Domenic Denicola
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by expressShawn Meng
 

Ähnlich wie Real Time Web with Node (20)

NodeJs
NodeJsNodeJs
NodeJs
 
JS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js AntipatternsJS Fest 2019 Node.js Antipatterns
JS Fest 2019 Node.js Antipatterns
 
Txjs
TxjsTxjs
Txjs
 
JSConf: All You Can Leet
JSConf: All You Can LeetJSConf: All You Can Leet
JSConf: All You Can Leet
 
Node.js - A Quick Tour
Node.js - A Quick TourNode.js - A Quick Tour
Node.js - A Quick Tour
 
node.js practical guide to serverside javascript
node.js practical guide to serverside javascriptnode.js practical guide to serverside javascript
node.js practical guide to serverside javascript
 
Node.js - Best practices
Node.js  - Best practicesNode.js  - Best practices
Node.js - Best practices
 
Node.js - A practical introduction (v2)
Node.js  - A practical introduction (v2)Node.js  - A practical introduction (v2)
Node.js - A practical introduction (v2)
 
Security Challenges in Node.js
Security Challenges in Node.jsSecurity Challenges in Node.js
Security Challenges in Node.js
 
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
JSLab. Домников Виталий. "ES6 генераторы и Koa.js"
 
Node.js - A Quick Tour II
Node.js - A Quick Tour IINode.js - A Quick Tour II
Node.js - A Quick Tour II
 
JavaScript - Like a Box of Chocolates
JavaScript - Like a Box of ChocolatesJavaScript - Like a Box of Chocolates
JavaScript - Like a Box of Chocolates
 
Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015Impress Your Friends with EcmaScript 2015
Impress Your Friends with EcmaScript 2015
 
Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010Websockets talk at Rubyconf Uruguay 2010
Websockets talk at Rubyconf Uruguay 2010
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Jersey framework
Jersey frameworkJersey framework
Jersey framework
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
 
Node.js
Node.jsNode.js
Node.js
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by express
 

Kürzlich hochgeladen

What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 

Kürzlich hochgeladen (20)

What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 

Real Time Web with Node

  • 1. REAL TIME WEB WITH NODE By Tim Caswell Saturday, June 5, 2010
  • 2. Live Interaction The web is about doing things, not just tourism anymore. Saturday, June 5, 2010
  • 3. Uses of Live Interaction Chat Widget Twitter Feed Stock Ticker Real-Time Game Collaborative Documents TXJS Demos Saturday, June 5, 2010
  • 4. Why we need non-blocking Polling is too slow and inefficient. Live interaction requires the server to push data. In order to push data, the connections need to be persistent. The server needs to handle thousands of persistent connections Threads aren’t going to scale. Saturday, June 5, 2010
  • 5. This is your server. (with blocking I/O) Saturday, June 5, 2010
  • 6. This is your server with low concurrency. Saturday, June 5, 2010
  • 7. This is your server with high concurrency. Saturday, June 5, 2010
  • 8. Connect We’ll use a new node framework that “connects” the web users to each other. Saturday, June 5, 2010
  • 9. It’s like Japanese Legos Saturday, June 5, 2010
  • 10. Pre-Built Blocks Connect.createServer([ {filter: "log"}, {filter: "body-decoder", route: "/stream"}, {provider: "pubsub", route: "/stream"}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root} ]); Saturday, June 5, 2010
  • 11. And easy too! Saturday, June 5, 2010
  • 12. method-override.js var key; // Initialize any state (on server startup) exports.setup = function (env) { key = this.key || "_method"; }; // Modify the request stream (on request) exports.handle = function(err, req, res, next){ if (key in req.body) { req.method = req.body[key].toUpperCase(); } next(); }; Saturday, June 5, 2010
  • 13. response-time.js exports.handle = function(err, req, res, next){ var start = new Date, writeHead = res.writeHead; res.writeHead = function(code, headers){ res.writeHead = writeHead; headers['X-Response-Time'] = (new Date - start) + "ms"; res.writeHead(code, headers); }; next(); }; Saturday, June 5, 2010
  • 14. Well, actually, it’s not always easy. Saturday, June 5, 2010
  • 16. static.js var fs = require('fs'), Url = require('url'), Path = require('path'); var lifetime = 1000 * 60 * 60; // 1 hour browser cache lifetime var DEFAULT_MIME = 'application/octet-stream'; module.exports = { setup: function (env) { this.root = this.root || process.cwd(); }, handle: function (err, req, res, next) { // Skip on error if (err) { next(); return; } var url = Url.parse(req.url); var pathname = url.pathname.replace(/..+/g, '.'), filename = Path.join(this.root, pathname); if (filename[filename.length - 1] === "/") { filename += "index.html"; } Saturday, June 5, 2010
  • 17. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; req.addListener("data", onData); module.exports = { req.addListener("end", onEnd); setup: function (env) { fs.stat(filename, function (err, stat) { this.root = this.root || process.cwd(); }, // Stop buffering events req.removeListener("data", onData); handle: function (err, req, res, next) {req.removeListener("end", onEnd); // Skip on error if (err) { // Fall through for missing files, thow error for other problems next(); if (err) { return; if (err.errno === process.ENOENT) { } next(); var url = Url.parse(req.url); // Refire the buffered events events.forEach(function (args) { req.emit.apply(req, args); var pathname = url.pathname.replace(/..+/g, '.'), }); filename = Path.join(this.root, pathname); return; if (filename[filename.length - 1] === "/") { filename += "index.html"; } Saturday, June 5, 2010
  • 18. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; req.addListener("data", onData); module.exports = { req.addListener("end", onEnd); (err); setup: function (env) { fs.stat(filename, function (err, stat) { rn; this.root = this.root || process.cwd(); }, // Stop buffering events req.removeListener("data", onData); req.removeListener("end", onEnd); the file directly using (err, req, res, next) { handle: function buffers ile(filename, function error data) { // Skip on (err, if (err) { // Fall through for missing files, thow error for other problems err) { next(); if (err) { next(err); return; if (err.errno === process.ENOENT) { return; } next(); var url = Url.parse(req.url); // Refire the buffered events writeHead(200, { events.forEach(function (args) { ontent-Type": Mime.type(filename), req.emit.apply(req, args); ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), var data.length, }); filename = Path.join(this.root, pathname); ast-Modified": stat.mtime.toUTCString(), return; / Cache in browser for 1 year ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 filename += "index.html"; end(data); } Saturday, June 5, 2010
  • 19. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), var events = []; Url = require('url'), function onData() { Path = require('path'); events.push(["data"].concat(Array.prototype.slice.call(arguments))); } var lifetime = 1000 * 60 * 60; // 1 hourfunction onEnd() { browser cache lifetime }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); } var DEFAULT_MIME = 'application/octet-stream'; // Mini mime module for static file serving req.addListener("data", onData); module.exports = { var req.addListener("end", onEnd); Mime = { (err); type: function getMime(path) (err, stat) { setup: function (env) { fs.stat(filename, function { rn; var index = path.lastIndexOf("."); this.root = this.root || process.cwd(); }, if (index < buffering events // Stop 0) { return DEFAULT_MIME; req.removeListener("data", onData); } req.removeListener("end", onEnd); the file directly using (err, req, res, next) { type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; handle: function buffers var ile(filename, // function error data) { Skip on (err, if (err) { return Fall through for missing files, thow error "; charset=utf-8" : type; // (/(text|javascript)/).test(type) ? type + for other problems err) { }, next(); if (err) { next(err); return; if (err.errno === process.ENOENT) { return; TYPES : { ".3gp" } next(); "video/3gpp", : var url = Url.parse(req.url); ".a" Refire the buffered events // : "application/octet-stream", writeHead(200, { ".ai" : "application/postscript", events.forEach(function (args) { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "audio/x-aiff", ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, ".aiff" : }); filename = Path.join(this.root, pathname); ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : / Cache in browser for 1 year ".asf" : "video/x-ms-asf", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".asm" : "text/x-asm", filename += "index.html"; ".asx" : "video/x-ms-asf", end(data); } ".atom" : "application/atom+xml", ".au" : "audio/basic", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 20. static.js // Buffer any events that fire while waiting on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".for" : function onData() { Url = require('url'), "text/x-fortran", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", } ".gemspec" : "text/x-script.ruby", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".gz" : "application/x-gzip", ".h" : "text/x-c", } var DEFAULT_MIME = 'application/octet-stream'; // Mini mime module for static file serving req.addListener("data", onData); ".hh" : "text/x-c", module.exports ".htm" = { var Mime = { : "text/html", req.addListener("end", onEnd); (err); ".html" : "text/html", rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { var index = path.lastIndexOf("."); this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { return DEFAULT_MIME; req.removeListener("data", onData); ".iso" : "application/octet-stream", } : req, res, next) {req.removeListener("end", onEnd); the file directly ".jar" (err,"application/java-archive", handle: function buffers using var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; ile(filename, // function error: data) { Skip".java" on (err, "text/x-java-source", if (err) { return Fall through for missing files, thow error "; charset=utf-8" : type; // (/(text|javascript)/).test(type) ? type + for other problems ".jnlp" : "application/x-java-jnlp-file", err) { }, next(); ".jpeg" : "image/jpeg", if (err) { next(err); return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { return; TYPES : { ".3gp" : "video/3gpp", } ".js" : "application/javascript", next(); var url = Url.parse(req.url); ".a" Refire the buffered events // : "application/octet-stream", writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "audio/x-aiff", ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", filename += : "application/mbox", ".mbox" "index.html"; ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 21. static.js // Buffer any events that fire while waiting "text/x-c", ".cc" : on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".chm" : "application/vnd.ms-htmlhelp", Url = require('url'), "text/x-fortran", ".for" : function onData() { ".class" : "application/octet-stream", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", ".com" : "application/x-msdownload", } ".gemspec" : "text/x-script.ruby", ".conf" : "text/plain", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime ".cpp" : "text/x-c", }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".crt" : "application/x-x509-ca-cert", ".gz" : "application/x-gzip", var DEFAULT_MIME = 'application/octet-stream'; ".h" : "text/x-c", } ".css" : "text/css", // Mini mime module for static file serving req.addListener("data", onData); ".csv" : "text/csv", ".hh" : "text/x-c", module.exports ".htm"= { var Mime = { : "text/html", req.addListener("end", onEnd); ".cxx" : "text/x-c", ".html" : "text/html", ".deb" : "application/x-debian-package", (err); rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { ".der" : "application/x-x509-ca-cert", var index = path.lastIndexOf(".");".diff" : "text/x-diff", this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { ".djv" : "image/vnd.djvu", return DEFAULT_MIME; req.removeListener("data", onData); ".djvu" : "image/vnd.djvu", ".iso" : "application/octet-stream", } : req, res, next) {req.removeListener("end", onEnd); ".dll" : "application/x-msdownload", the file directly ".jar" (err,"application/java-archive", handle: function buffers using var type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; // function error: data) { Skip".java" on (err, "text/x-java-source", ".dmg" : "application/octet-stream", ile(filename, return Fall through for missing files, thow error "; charset=utf-8" : type; if (err) { // (/(text|javascript)/).test(type) ? : "application/msword", ".jnlp" : "application/x-java-jnlp-file", ".doc" type + for other problems err) { }, next(); ".jpeg" : "image/jpeg", if (err) { ".dot" : "application/msword", next(err); return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { "application/xml-dtd", ".dtd" : return; TYPES : { ".3gp" : "video/3gpp", ".dvi" } ".js" : "application/javascript", next(); : "application/x-dvi", var url = Url.parse(req.url); ".a" Refire the buffered events : "application/java-archive", // : "application/octet-stream", ".ear" writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) : "message/rfc822", ".eml" { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "application/postscript", : "audio/x-aiff", ".eps" ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ".exe" : "application/x-msdownload", ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : ".f" : "text/x-fortran", / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ".f77" : "text/x-fortran", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", ".f90" filename += : "application/mbox", ".mbox" "index.html"; : "text/x-fortran", ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 22. static.js // Buffer any events that fire while waiting "text/x-c", ".cc" : on the stat. var fs = require('fs'), : "video/x-flv",var events = []; ".flv" ".chm" : "application/vnd.ms-htmlhelp", Url = require('url'), "text/x-fortran", ".for" : function onData() { ".class" : "application/octet-stream", Path = require('path'); ".gem" events.push(["data"].concat(Array.prototype.slice.call(arguments))); : "application/octet-stream", ".com" : "application/x-msdownload", } ".gemspec" : "text/x-script.ruby", ".conf" : "text/plain", var lifetime = ".gif" 60: *"image/gif", function onEnd() { 1000 * 60; // 1 hour browser cache lifetime ".cpp" : "text/x-c", }; events.push(["end"].concat(Array.prototype.slice.call(arguments))); ".crt" : "application/x-x509-ca-cert", ".gz" : "application/x-gzip", var DEFAULT_MIME = 'application/octet-stream'; ".h" : "text/x-c", } ".css" : "text/css", // Mini mime module for static file serving req.addListener("data", onData); ".csv" : "text/csv", ".hh" : "text/x-c", module.exports ".htm"= { var Mime = { : "text/html", req.addListener("end", onEnd); ".cxx" : "text/x-c", ".html" : "text/html", ".deb" : "application/x-debian-package", (err); rn; setup: function (env) "image/vnd.microsoft.icon", getMime(path) (err, stat) { ".ico" : { type: function fs.stat(filename, function { ".der" : "application/x-x509-ca-cert", var index = path.lastIndexOf(".");".diff" : "text/x-diff", this.root = this.root || process.cwd(); ".ics" : "text/calendar", }, ".ifb" : "text/calendar", (index < buffering events if // Stop 0) { ".djv" : "image/vnd.djvu", return DEFAULT_MIME; req.removeListener("data", onData); ".djvu" : "image/vnd.djvu", ".iso" : "application/octet-stream", } req.removeListener("end", onEnd); ".dll" : "application/x-msdownload", the file directly ".jar"".bmp" req, "image/bmp", type = Mime.TYPES[path.substring(index).toLowerCase()] || DEFAULT_MIME; handle: function (err,"application/java-archive", using buffers : res, next) { : var // function error: data) { Skip".java" on (err, "text/x-java-source", ".bz2" : "application/x-bzip2", ".dmg" : "application/octet-stream", ile(filename, return (/(text|javascript)/).test(type) ? : "application/msword", : type; type + "; charset=utf-8" err) { if (err) { : "text/x-c", // Fall through for missing files, thow error for other problems ".jnlp" : "application/x-java-jnlp-file", ".c" ".doc" }, if (err) { ".dot" : "application/msword", next(err); next(); ".cab" ".jpeg" : "image/jpeg", : "application/vnd.ms-cab-compressed", return; ".jpg" : "image/jpeg", if (err.errno === process.ENOENT) { "application/xml-dtd", ".dtd" : return; TYPES : { ".3gp" : "video/3gpp", ".dvi" } ".js" : "application/javascript", next(); : "application/x-dvi", var url = Url.parse(req.url); ".a" Refire the buffered events : "application/java-archive", // : "application/octet-stream", ".ear" writeHead(200, { ".json" : "application/json", ".ai" : "application/postscript", ".log" : "text/plain", events.forEach(function (args) : "message/rfc822", ".eml" { ontent-Type": Mime.type(filename), ".aif" req.emit.apply(req, args); : "application/postscript", : "audio/x-aiff", ".eps" ontent-Length": pathname = url.pathname.replace(/..+/g, '.'), "audio/x-aiff", var data.length, "audio/x-mpegurl", ".m3u" : ".aiff" : filename = Path.join(this.root, pathname); ".m4v" : "video/mp4", }); ".exe" : "application/x-msdownload", ast-Modified": stat.mtime.toUTCString(), ".asc" return; "application/pgp-signature", : ".f" : "text/x-fortran", / Cache in browser ".man" year "text/troff", for 1 : ".asf" : "video/x-ms-asf", ".f77" : "text/x-fortran", ache-Control": (filename[filename.length - 1] === "/") { if "public max-age=" + 31536000 ".mathml" : "application/mathml+xml", ".asm" : "text/x-asm", ".f90" filename += : "application/mbox", ".mbox" "index.html"; : "text/x-fortran", ".asx" : "video/x-ms-asf", end(data); } ".mdoc" : "text/troff", ".me" : "text/troff", ".atom" : "application/atom+xml", ".au" : "audio/basic", ".mid" : "audio/midi", Saturday, June 5, 2010 ".avi" : "video/x-msvideo",
  • 23. Built-in Filter Modules Authentication Error Handler Authorization Gzip Body Decoder Log Cache Method Override Conditional Get Response Time Debug Session Saturday, June 5, 2010
  • 24. Built-in Data Providers Static Cache Manifest Rest Direct Router JSON-RPC PubSub More... Saturday, June 5, 2010
  • 26. app.js (stack) require.paths.unshift("./lib"); var Connect = require('connect'); var root = __dirname + "/public"; module.exports = Connect.createServer([ {filter: "log"}, {filter: "body-decoder"}, {provider: "pubsub", route: "/stream", logic: Backend}, {filter: "conditional-get"}, {filter: "cache"}, {filter: "gzip"}, {provider: "cache-manifest", root: root}, {provider: "static", root: root} ]); Saturday, June 5, 2010
  • 27. app.js (Backend) var Backend = { subscribe: function (subscriber) { if (subscribers.indexOf(subscriber) < 0) { subscribers.push(subscriber); } }, unsubscribe: function (subscriber) { var pos = subscribers.indexOf(subscriber); if (pos >= 0) { subscribers.slice(pos); } }, publish: function (message, callback) { subscribers.forEach(function (subscriber) { subscriber.send(message); }); callback(); } }; Saturday, June 5, 2010
  • 28. http://github.com/extjs/connect http://twitter.com/creationix http://raphaeljs.com http://nodejs.org Saturday, June 5, 2010
  • 29. Any Questions ? Saturday, June 5, 2010