Server side JavaScript: going all the way

Preview:

DESCRIPTION

Node has captured the attention of early adopters by clearly differentiating itself as being asynchronous from the ground up while remaining accessible. Now that server side JavaScript is at the cutting edge of the asynchronous, real time web, it is in a much better position to establish itself as the go to language for also making synchronous, CRUD webapps and gain a stronger foothold on the server. This talk covers the current state of server side JavaScript beyond Node. It introduces Common Node, a synchronous CommonJS compatibility layer using node-fibers which bridges the gap between the different platforms. We look into Common Node's internals, compare its performance to that of other implementations such as RingoJS and go through some ideal use cases.

Citation preview

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

Node.js CommonJS Wakanda RingoJS v8cgi Narwhal Common Node Flussperfd Akshell GPSEE0

2000

4000

6000

8000

10000

12000

Project Followers

Group Members

Fo

llow

ers

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

processthread stack

t →

Common Node (fibers)

processfiber 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

Benchmarksab -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("ascii");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

hello-world string-alloc parse-json static-file set-timeout0

1000

2000

3000

4000

5000

6000

Node.js

Common Node

RingoJS

SyncJS Fragmentation

Wakanda RingoJS v8cgi Narwhal Common Node Flussperfd Akshell GPSEE0

50

100

150

200

250

300

350

400

Project Followers

Group Members

Fo

llow

ers

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

Recommended