Introduction to Nodejs
-
Upload
gabriele-lana -
Category
Technology
-
view
14.275 -
download
5
description
Transcript of Introduction to Nodejs
verona21/05/2011
“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!"# [email protected] $ !"" [email protected] $ %"" [email protected] %"" [email protected]%"" [email protected]
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?