Server side JavaScript: going all the way #rejectjs 29.09.11
/me #startups #akshell #ringojs #moscowjs #dailyjs
 
Node.js Ideal use cases Real time apps, infrastructure duct tape Badly suited for CRUD, command line tools, CPU heavy loads Easy to get started with Difficult to get into production
 
Akshell NarwhalJS RingoJS Wakanda GPSEE v8cgi
ServerJS Fragmentation
Sync vs. Async
 
“ async style is kicking sync style's ass”
 
sync is “on top of” async –  a higher level of abstraction
Sync   vs.  Async   function add(callback) { http.get(url1, function(response1) { var part1 = response1.data; http.get(url2, function(response2) { var part2 = response2.data; callback(part1 + part2); } } }
Sync  vs.   Async http.get(url1).data + http.get(url2).data
Interoperability Pure JavaScript modules run anywhere Templating, parsing, formatting, encoding Anything doing I/O exposes sync or async API Defines the interface exposed by higher level packages
 
CommonJS /1 Assert Console System/1.0
CommonJS /2 Binary/B IO/A Filesystem/A JSGI 0.3 HTTP Client /A Sockets/A Subprocess
common-node Implements  synchronous  CommonJS proposals using node-fibers  Traceur to support latest language features Bridges the gap between platforms, sync and async
What it's good for?
everything!
What's it  actually  good for? Business logic Lots of state, fine grained error handling CRUD Java, Rails, PHP, Python Command line tools Cross platform portability
On Threads & Fibers “ Threads suck”  - Brendan Eich, creator of JavaScript “ Fibers introduce interleaving hazards — Any function call can cause a yield and then your closure invariants *may be* broken.” - Kris Kowal, creator of CommonJS/Modules
Concurrency in JavaScript “ You shouldn’t think that event-based concurrency eliminates synchronization, or shared memory, or anything other than preemption” - Sam Tobin-Hochstadt, member of the Ecma TC39 committee on JavaScript
fibers /1 Co routine implementation using libcoro  Co-operative multitasking Implicit synchronization
fibers /2 Not a fork or a hack of Node No wrapper script required V8 Context and 64KB stack per fiber Will run on Windows
Node.js process [ closure closure t  ->
RingoJS (0.8) process thread  stack process thread  stack t  ->
Common Node (fibers) process fiber stack fiber stack [ t  ->
Comparison Node.js Common Node RingoJS Process Count Single Single Multiple State Closure Fiber Stack Thread Stack Multitasking User (co-op) Library (co-op) OS (pre-empt) Memory Usage Low Low High “ Jitter” High High Low
Internals - sleep exports.sleep = function(milliseconds) { var fiber = Fiber.current; setTimeout(function() { fiber.run(); }, milliseconds); yield(); } ;
Internals – HttpClient /1 var req = http.request(options, function(r) { fiber.run(r); }); req.on('error', function(error) { fiber.run(error); }); this.guts.body.forEach(function(block) { req.write(block.buffer || block); }); req.end();
Internals – HttpClient /2 var result = yield(); if(result instanceof Error) throw new Error(result.message); return { status: result.statusCode, headers: result.headers, body: new Stream(result) };
Internals - IO // on 'data', 'end', 'error' // pause when draining var listeners = attach(this.stream); var data = yield(); detach(this.stream, listeners);
Examples
JSGI exports.app = function(request) { return { status: 200, headers: {}, body: ['Hello World!']  // openRaw(module.filename) }; };
Spawn & Sleep exports.app = function(request) { spawn(function() { sleep(10000); console.log('Hello Server!'); }); return { status: 200, headers: {}, body: ['Hello Client!'] }; };
HTTP Proxy var HttpClient = require('httpclient').HttpClient; exports.app = function(req) { req.url = 'http://nodejs.org'; return new HttpClient(req).finish(); };
Twitter Streaming /1 var stream = new TextStream(   new HttpClient({ method:  'POST', url:  '...', headers: {} body: ['track='+system.args[4]], timeout: 10000 }).finish().body);
Twitter Streaming /2 var line; while(true) { line = stream.readLine(); if(!line.length) break; if(line.length > 1) { var message = JSON.parse(line); console.log(message.text); } }
Telnet Chat /1 var socket = require('socket'); var server = new socket.Socket();  var clients = [], client; server.bind('localhost', 23);
Telnet Chat /2 while(true) { clients.push(client = server.accept().getStream()); spawn(function() {   var stream = client, line; while((line = stream.read(null)).length) { clients.forEach(function(c) { if(stream != c) c.write(line); }); } clients.splice(clients.indexOf(stream), 1); }); }
Demo telnet 10.0.0.97
Benchmarks ab -n 50000 -c 50
hello-world exports.app = function() { return { status: 200, headers: { 'Content-Type': 'text/plain' }, body: ['Hello World!\n'] }; };
 
string-alloc exports.app = function(request) { for( var i = 1; i <= 50; i++) b.decodeToString(&quot;ascii&quot;); return { status: 200, headers: {}, body: [b] }; };
 
parse-json exports.app = function(request) { JSON.parse(json); return { status: 200, headers: {}, body: [json] }; };
 
static-file exports.app = function() { return {  status: 200,  headers: {},  body:  openRaw('../README.md') }; };
 
set-timeout exports.app = function() { sleep(100); return { status: 200, headers: {}, body: [] }; };
 
Throughput
SyncJS Fragmentation
Road map common-utils tests in separate project narhwal-mongodb fork hns/stick fork higher level packages (see project wiki)
Contributing Google  “ common node ” github.com/olegp/common-node/ npm -g install common-node
Thank you! @olegpodsechin

Server side JavaScript: going all the way

  • 1.
    Server side JavaScript:going all the way #rejectjs 29.09.11
  • 2.
    /me #startups #akshell#ringojs #moscowjs #dailyjs
  • 3.
  • 4.
    Node.js Ideal usecases Real time apps, infrastructure duct tape Badly suited for CRUD, command line tools, CPU heavy loads Easy to get started with Difficult to get into production
  • 5.
  • 6.
    Akshell NarwhalJS RingoJSWakanda GPSEE v8cgi
  • 7.
  • 8.
  • 9.
  • 10.
    “ async styleis kicking sync style's ass”
  • 11.
  • 12.
    sync is “ontop of” async – a higher level of abstraction
  • 13.
    Sync vs. Async function add(callback) { http.get(url1, function(response1) { var part1 = response1.data; http.get(url2, function(response2) { var part2 = response2.data; callback(part1 + part2); } } }
  • 14.
    Sync vs. Async http.get(url1).data + http.get(url2).data
  • 15.
    Interoperability Pure JavaScriptmodules run anywhere Templating, parsing, formatting, encoding Anything doing I/O exposes sync or async API Defines the interface exposed by higher level packages
  • 16.
  • 17.
    CommonJS /1 AssertConsole System/1.0
  • 18.
    CommonJS /2 Binary/BIO/A Filesystem/A JSGI 0.3 HTTP Client /A Sockets/A Subprocess
  • 19.
    common-node Implements synchronous CommonJS proposals using node-fibers Traceur to support latest language features Bridges the gap between platforms, sync and async
  • 20.
  • 21.
  • 22.
    What's it actually good for? Business logic Lots of state, fine grained error handling CRUD Java, Rails, PHP, Python Command line tools Cross platform portability
  • 23.
    On Threads &Fibers “ Threads suck” - Brendan Eich, creator of JavaScript “ Fibers introduce interleaving hazards — Any function call can cause a yield and then your closure invariants *may be* broken.” - Kris Kowal, creator of CommonJS/Modules
  • 24.
    Concurrency in JavaScript“ You shouldn’t think that event-based concurrency eliminates synchronization, or shared memory, or anything other than preemption” - Sam Tobin-Hochstadt, member of the Ecma TC39 committee on JavaScript
  • 25.
    fibers /1 Coroutine implementation using libcoro Co-operative multitasking Implicit synchronization
  • 26.
    fibers /2 Nota fork or a hack of Node No wrapper script required V8 Context and 64KB stack per fiber Will run on Windows
  • 27.
    Node.js process [closure closure t ->
  • 28.
    RingoJS (0.8) processthread stack process thread stack t ->
  • 29.
    Common Node (fibers)process fiber stack fiber stack [ t ->
  • 30.
    Comparison Node.js CommonNode RingoJS Process Count Single Single Multiple State Closure Fiber Stack Thread Stack Multitasking User (co-op) Library (co-op) OS (pre-empt) Memory Usage Low Low High “ Jitter” High High Low
  • 31.
    Internals - sleepexports.sleep = function(milliseconds) { var fiber = Fiber.current; setTimeout(function() { fiber.run(); }, milliseconds); yield(); } ;
  • 32.
    Internals – HttpClient/1 var req = http.request(options, function(r) { fiber.run(r); }); req.on('error', function(error) { fiber.run(error); }); this.guts.body.forEach(function(block) { req.write(block.buffer || block); }); req.end();
  • 33.
    Internals – HttpClient/2 var result = yield(); if(result instanceof Error) throw new Error(result.message); return { status: result.statusCode, headers: result.headers, body: new Stream(result) };
  • 34.
    Internals - IO// on 'data', 'end', 'error' // pause when draining var listeners = attach(this.stream); var data = yield(); detach(this.stream, listeners);
  • 35.
  • 36.
    JSGI exports.app =function(request) { return { status: 200, headers: {}, body: ['Hello World!'] // openRaw(module.filename) }; };
  • 37.
    Spawn & Sleepexports.app = function(request) { spawn(function() { sleep(10000); console.log('Hello Server!'); }); return { status: 200, headers: {}, body: ['Hello Client!'] }; };
  • 38.
    HTTP Proxy varHttpClient = require('httpclient').HttpClient; exports.app = function(req) { req.url = 'http://nodejs.org'; return new HttpClient(req).finish(); };
  • 39.
    Twitter Streaming /1var stream = new TextStream( new HttpClient({ method: 'POST', url: '...', headers: {} body: ['track='+system.args[4]], timeout: 10000 }).finish().body);
  • 40.
    Twitter Streaming /2var line; while(true) { line = stream.readLine(); if(!line.length) break; if(line.length > 1) { var message = JSON.parse(line); console.log(message.text); } }
  • 41.
    Telnet Chat /1var socket = require('socket'); var server = new socket.Socket(); var clients = [], client; server.bind('localhost', 23);
  • 42.
    Telnet Chat /2while(true) { clients.push(client = server.accept().getStream()); spawn(function() { var stream = client, line; while((line = stream.read(null)).length) { clients.forEach(function(c) { if(stream != c) c.write(line); }); } clients.splice(clients.indexOf(stream), 1); }); }
  • 43.
  • 44.
    Benchmarks ab -n50000 -c 50
  • 45.
    hello-world exports.app =function() { return { status: 200, headers: { 'Content-Type': 'text/plain' }, body: ['Hello World!\n'] }; };
  • 46.
  • 47.
    string-alloc exports.app =function(request) { for( var i = 1; i <= 50; i++) b.decodeToString(&quot;ascii&quot;); return { status: 200, headers: {}, body: [b] }; };
  • 48.
  • 49.
    parse-json exports.app =function(request) { JSON.parse(json); return { status: 200, headers: {}, body: [json] }; };
  • 50.
  • 51.
    static-file exports.app =function() { return { status: 200, headers: {}, body: openRaw('../README.md') }; };
  • 52.
  • 53.
    set-timeout exports.app =function() { sleep(100); return { status: 200, headers: {}, body: [] }; };
  • 54.
  • 55.
  • 56.
  • 57.
    Road map common-utilstests in separate project narhwal-mongodb fork hns/stick fork higher level packages (see project wiki)
  • 58.
    Contributing Google “ common node ” github.com/olegp/common-node/ npm -g install common-node
  • 59.