Introduction to Nodejs

Post on 15-Jan-2015

14.275 views 5 download

description

Introduction to Nodejs with examples

Transcript of Introduction to Nodejs

verona21/05/2011

gabriele lanagabriele.lana@cleancode.it

twitter: @gabrielelana

“Node's goal is to provide an easy

way to build scalable network

programs”

http://nodejs.org/#about

why node.js?

what is node.js?

• asynchronous i/o framework• core in c++ on top of v8• rest of it in javascript• swiss army knife for network

related stuffs• can handle thousands of

concurrent connections with minimal overhead (cpu/memory) on a single process

Single threadsynchronous I/0

Single threadsynchronous I/0

multiple threadsynchronous I/0

multiple threadsynchronous I/0

you can“always”

scale withmultiple

machines butit costsyou $$$

but...what is HEdoing?

but...what is HEdoing?

CPU BOUNDTASKS?

but...what is HEdoing?

CPU BOUNDTASKS?

...OR I/oBOUNDTASKS?

function productsInCart(request, response) { var db = new Db() var user = new User(request) if (user.isAuthorized("cart/products")) { response.write( JSON.stringify( db.productsInCart(user.cartId()) ) ) } else { response.unauthorized() }}

synchronous I/0

function productsInCart(request, response) { var db = new Db() var user = new User(request) if (user.isAuthorized("cart/products")) { response.write( JSON.stringify( db.productsInCart(user.cartId()) ) ) } else { response.unauthorized() }}

synchronous I/0

function productsInCart(request, response) { var db = new Db() var user = new User(request) if (user.isAuthorized("cart/products")) { response.write( JSON.stringify( db.productsInCart(user.cartId()) ) ) } else { response.unauthorized() }}

synchronous I/0

function productsInCart(request, response) { var db = new Db() var user = new User(request) if (user.isAuthorized("cart/products")) { response.write( JSON.stringify( db.productsInCart(user.cartId()) ) ) } else { response.unauthorized() }}

synchronous I/0

single threadasynchronous I/0

single threadasynchronous I/0

single threadasynchronous I/0

single threadasynchronous I/0

I am“callback”

call meif you

need me...

Event Emitter

var http = require("http")

var server = http.createServer(function(request, response) { response.writeHead(200, { "Content-Type": "plain/text" }) response.write("Hello World\n") response.end()})

server.listen(8080)

console.log("> SERVER STARTED")

hello

_w

orld_server.j

s

Event Emitter

var http = require("http")

var server = http.createServer(function(request, response) { response.writeHead(200, { "Content-Type": "plain/text" }) response.write("Hello World\n") response.end()})

server.listen(8080)

console.log("> SERVER STARTED")

hello

_w

orld_server.j

s

Event Emitter

var http = require("http")

var server = http.createServer(function(request, response) { response.writeHead(200, { "Content-Type": "plain/text" }) response.write("Hello World\n") response.end()})

server.listen(8080)

console.log("> SERVER STARTED")

hello

_w

orld_server.j

s

Event Emitter

var http = require("http")

var server = http.createServer(function(request, response) { response.writeHead(200, { "Content-Type": "plain/text" }) response.write("Hello World\n") response.end()})

server.listen(8080)

console.log("> SERVER STARTED")

hello

_w

orld_server.j

s

coder@apollo:~/Work/src/node/examples$ node hello_world_server.js> SERVER STARTED

coder@apollo:~$ curl "http://localhost:8080/"Hello World

#1

#2

Event Emitter

var server = require("http").createServer()

server.on("request", function(request, response) { console.log("> REQUEST STARTED") request.on("end", function() { console.log("> REQUEST CLOSED") response.writeHead(200, { "Content-Type": "plain/text" }) response.end("Hello World\n") server.close() }) response.on("close", function() { console.log("> RESPONSE CLOSED") })})h

ello

_w

orld_server.j

sEvent Emitter

...

server.on("close", function() { console.log("> SERVER CLOSED")})

server.on("listening", function() { console.log("> SERVER STARTED")})

server.listen(8080)

hello

_w

orld_server.j

sEvent Emitter

coder@apollo:~/Work/src/node/examples$ node hello_world_server.js> SERVER STARTED

coder@apollo:~$ curl "http://localhost:8080/"Hello World

> REQUEST STARTED> REQUEST CLOSED> SERVER CLOSED

#1

#2

#1

Event Emitter

why socomplicated?

server.on("request", function(request, response) { var chunks = [], output = fs.createWriteStream("./output")

request.on("data", function(chunk) { chunks = forEachLine(chunks.concat(chunk), function(line) { output.write(parseInt(line, 10) * 2) output.write("\n") }) })

request.on("end", function() { response.writeHead(200, { "Content-Type": "plain/text" }) response.end("OK\n") output.end() server.close() })})

pro

xy_stream

.jsdata streamS

coder@apollo:~/Work/src/node/examples$ node stream_doubler.js

coder@apollo:~$ curl "http://localhost:8080/" --data $'1\n2\n3\n'OK

coder@apollo:~/Work/src/node/examples$ cat output 246

#1

#2

#1

data streamS

why javascript?

• Friendly callbacks• ubiquitous (well known)• no I/o primitives• one language to rule them all

mind shift #1

application

application

application

application

Web s

erver

web applications before: a web

server with some application logic

mind shift #1

web applications after: an application

accessible over http

web s

erver

application

mind shift #1

web applications after: an application

that can communicate and collaborate with

the worldw

eb s

erver

application

mind shift #2

web applications before: stateful

• no easy to scale• no easy to reuse

M

c

v

mind shift #2

web applications before: stateful

• no easy to scale• no easy to reuse

applicationstate

conversationstate

M

c

v

mind shift #2

web applications before: stateful

• no easy to scale• no easy to reuse

M

c

v

tightly coupled

mind shift #2

web applications after: stateless

• easy to scale• easy to reuse

http M

c

v

mind shift #2

web applications after: stateless

• easy to scale• easy to reuse

http M

c

v

conversationstate

applicationstate

mind shift #2

web applications after: stateless

• easy to scale• easy to reuse

http

M

http

web s

erver

statefullconnection

M

no fluffjust stuff

What aboutcpu bound

tasks?

the forkbe with you

#!/bin/bash

for count in `seq 1 100`; do echo $count sleep 0.1done

lo

ng_runnin

g_jo

b.s

h

the forkbe with you

var spawn = require("child_process").spawn, server = require("http").createServer()

server.on("request", function(request, response) { var job = spawn("./long_running_job.sh")

job.stdout.on("data", function(tick) { response.write(tick) })

job.on("exit", function() { response.end() })})

lo

ng_runnin

g_server.j

s

the forkbe with you

coder@apollo:~$ ab -c 1 -n 1 "http://localhost:8080/"...Concurrency Level: 1Time taken for tests: 10.531 seconds...

coder@apollo:~$ ab -c 1 -n 2 "http://localhost:8080/"...Concurrency Level: 1Time taken for tests: 20.108 seconds...

the forkbe with you

coder@apollo:~$ ab -c 2 -n 1 "http://localhost:8080/"...Concurrency Level: 2Time taken for tests: 10.634 seconds...

coder@apollo:~$ ab -c 100 -n 100 "http://localhost:8080/"...Concurrency Level: 100Time taken for tests: 11.198 seconds...

coder@apollo:~$ ab -c 500 -n 500 "http://localhost:8080/"...Concurrency Level: 500Time taken for tests: 31.082 seconds...

enter comet

watc

h

Mwatch

watch

spawn

watch

watc

h

watc

h

enter comet

demo

INSTALL NPM(node packet manager)

coder@apollo:~/Work/src/node/examples$ curl http://npmjs.org/install.sh | sh...npm okIt worked

coder@apollo:~/Work/src/node/examples$ npm search | wc -l1918

coder@apollo:~/Work/src/node/examples$ npm install connect socket.io underscorecoder@apollo:~/Work/src/node/examples$ npm list!"# connect@1.4.1 $ !"" mime@1.2.2 $ %"" qs@0.1.0 %"" socket.io@0.6.17%"" underscore@1.1.6

pro

gress_server.j

s

var server = connect( connect.favicon(), connect.logger({"buffer": true}), connect.router(function(resource) { resource.post("/spawn", function(request, response) { ... }) }), connect.static(path.join(__dirname, "public"), {"cache": true}))

server/routing

pro

gress_server.j

s resource.post("/spawn", function(request, response) { var job = spawn(path.join(__dirname, "bin", "job.sh")), id = ++counter response.writeHead(202, {"Content-Type": "plain/text"}) response.end("OK\n")

job.stdout.on("data", function(output) { var progress = parseInt(output.toString(), 10) _(connections).each(function(connection) { connection.send({"id": id, "progress": progress}) }) }) })

server/routing

pro

gress_server.j

s

io.listen(server).on("connection", function(client) {

connections[client.sessionId] = client

client.on("disconnect", function() { delete connections[client.sessionId] })

})

server/socket.io

pro

gress_server.j

s server.listen(port, host, function() { var spawnJobsInterval = setInterval(function() { http.request({ "port": port, "host": host, "method": "POST", "path": "/spawn" }).end() }, Math.floor(Math.random()*2000)+500)

server.on("close", function() { clearInterval(spawnJobsInterval) process.exit() })})

process.on("SIGINT", function() { server.close()})

server/simulation

<html> <head> <title>Node.js - Long Running Jobs</title> <link href="css/style.css" rel="stylesheet" /> <script type="text/javascript" src="/socket.io/socket.io.js"></script> <script type="text/javascript" src="js/jquery-1.6.0.js"></script> <script type="text/javascript" src="js/progress.js"></script> </head> <body> <div id="template"> <div class="progress-container"> <span class="progress-text">JOB/? - ?%</span> <div class="progress-bar"></div> </div> </div> <div id="container"> </div> </body> <script> ... </script></html>

interface/htmlin

dex.h

tml

<html> ... <script> $(function() { var socket = new io.Socket()

socket.on("connect", function() { }) socket.on("disconnect", function() { }) socket.on("message", function(job) { $("#template").progressBar(job.id, job.progress) })

socket.connect() }) </script></html>

interface/socket.io

index.h

tml

$.fn.progressBar = function(id, progress) { var $template = $(this) $("#job-" + id) .otherwise(function() { return $template.children().clone() .attr("id", "job-" + id) .find(".progress-text").text("JOB/" + id + " - 0%").end() .appendTo("#container") }) .find(".progress-text").text("JOB/" + id + " - " + progress + "%").end() .find(".progress-bar").css("width", progress + "%").end() .tap(function() { if (progress === 100) { $(this) .find(".progress-bar").css("background-color", "red").end() .after(500, function() { $(this).fadeOut("slow", function() { $(this).remove() }) }) }

interface/progress bar

pro

gress.j

s

$.fn.otherwise = function(ifNotFound) { if (this.length === 0) { return ifNotFound() } return this}

$.fn.after = function(milliseconds, doSomething) { var self = this setTimeout(function() { doSomething.apply(self) }, milliseconds) return this}

$.fn.tap = function() { var fn = Array.prototype.shift.apply(arguments) fn.apply(this, arguments) return this}

interface/progress bar

pro

gress.j

s

single threadasynchronous I/0

we can do itwith a littlemore “style”

pro

gress_server.j

s

var Job = (function() { var EventEmitter = require("events").EventEmitter, spawn = require("child_process").spawn, inherits = require("util").inherits

function Job(id) { EventEmitter.call(this) this.id = id }

inherits(Job, EventEmitter)

Job.prototype.run = function() { return _(this).tap(function(job) { spawn(path.join(__dirname, "bin", "job.sh")) .stdout.on("data", function(output) { job.emit("progress", job.id, parseInt(output.toString(), 10)) }) }) } return Job})()

server/event emitter

pro

gress_server.j

s

resource.post("/spawn", function(request, response) { new Job(++counter) .on("progress", function(id, progress) { _(connections).each(function(connection) { connection.send({"id": id, "progress": progress}) }) }) .run()

response.writeHead(202, { "Content-Type": "plain/text" }) response.end("OK\n")})

server/event emitter

node.js is awesome but when should i use it?

• chat/messaging• real-time applications• intelligent proxies• high concurrency applications• communication hubs• coordinators

when to use it?

please tell me somethingbad aboutnode.js

some warnings

• release stable 0.4.7 (young)• lots of stuffs to look at• lots of half backed stuffs• retro compatibility???• bad at handling static contents• Bad at handling binary contents• hard to find organized and

authoritative informations

vs

Questions?

gabriele lanagabriele.lana@cleancode.it

twitter: @gabrielelana

http://joind.in/3359