Introduction to Nodejs
- 3. why
node.js?
“Node's goal is to
provide an easy
way to build
scalable network
programs”
http://nodejs.org/#about
- 4. 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
- 9. you can
“always”
scale with
multiple
machines but
it costs
you $$$
- 12. CPU BOUND
TASKS?
but...
what is HE
doing?
- 13. CPU BOUND
TASKS?
...OR I/o
but... BOUND
what is HE TASKS?
doing?
- 14. 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()
}
}
- 15. 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()
}
}
- 16. 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()
}
}
- 17. 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()
}
}
- 20. hello_world_server.js
Event Emitter
var http = require("http")
var server = http.createServer(function(request, response) {
response.writeHead(200, {
"Content-Type": "plain/text"
})
response.write("Hello Worldn")
response.end()
})
server.listen(8080)
console.log("> SERVER STARTED")
- 21. hello_world_server.js
Event Emitter
var http = require("http")
var server = http.createServer(function(request, response) {
response.writeHead(200, {
"Content-Type": "plain/text"
})
response.write("Hello Worldn")
response.end()
})
server.listen(8080)
console.log("> SERVER STARTED")
- 22. hello_world_server.js
Event Emitter
var http = require("http")
var server = http.createServer(function(request, response) {
response.writeHead(200, {
"Content-Type": "plain/text"
})
response.write("Hello Worldn")
response.end()
})
server.listen(8080)
console.log("> SERVER STARTED")
- 23. hello_world_server.js
Event Emitter
var http = require("http")
var server = http.createServer(function(request, response) {
response.writeHead(200, {
"Content-Type": "plain/text"
})
response.write("Hello Worldn")
response.end()
})
server.listen(8080)
console.log("> SERVER STARTED")
- 25. Event Emitter
var server = require("http").createServer()
hello_world_server.js
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 Worldn")
server.close()
})
response.on("close", function() {
console.log("> RESPONSE CLOSED")
})
})
- 26. hello_world_server.js
Event Emitter
...
server.on("close", function() {
console.log("> SERVER CLOSED")
})
server.on("listening", function() {
console.log("> SERVER STARTED")
})
server.listen(8080)
- 29. data streamS
server.on("request", function(request, response) {
var chunks = [],
output = fs.createWriteStream("./output")
proxy_stream.js
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("OKn")
output.end()
server.close()
})
})
- 31. why
javascript?
• Friendly callbacks
• ubiquitous (well known)
• no I/o primitives
• one language to rule them all
- 32. web applications
before: a web
mind shift #1 server with some
application logic
application
Web server
application
application
application
- 33. web applications
after: an application
mind shift #1 accessible over
http
web server
application
- 34. web applications
after: an application
mind shift #1 that can
communicate and
collaborate with
the world
web server
application
- 35. web applications
before: stateful
mind shift #2 • no easy to scale
• no easy to reuse
v
M
c
- 36. web applications
before: stateful
mind shift #2 • no easy to scale
• no easy to reuse
v
M
c
conversation application
state state
- 37. web applications
before: stateful
mind shift #2 • no easy to scale
• no easy to reuse
v
M
c
tightly coupled
- 38. web applications
after: stateless
mind shift #2 • easy to scale
• easy to reuse
v
http
M
c
- 39. web applications
after: stateless
mind shift #2 • easy to scale
• easy to reuse
v
http
M
c
conversation application
state state
- 40. web applications
after: stateless
mind shift #2
http
web server
M
http
statefull
connection
• easy to scale M
• easy to reuse
- 44. the fork
long_running_server.js be 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()
})
})
- 45. the fork
be with you
coder@apollo:~$ ab -c 1 -n 1 "http://localhost:8080/"
...
Concurrency Level: 1
Time taken for tests: 10.531 seconds
...
coder@apollo:~$ ab -c 1 -n 2 "http://localhost:8080/"
...
Concurrency Level: 1
Time taken for tests: 20.108 seconds
...
- 46. the fork
be with you
coder@apollo:~$ ab -c 2 -n 1 "http://localhost:8080/"
...
Concurrency Level: 2
Time taken for tests: 10.634 seconds
...
coder@apollo:~$ ab -c 100 -n 100 "http://localhost:8080/"
...
Concurrency Level: 100
Time taken for tests: 11.198 seconds
...
coder@apollo:~$ ab -c 500 -n 500 "http://localhost:8080/"
...
Concurrency Level: 500
Time taken for tests: 31.082 seconds
...
- 49. INSTALL NPM
(node packet manager)
coder@apollo:~/Work/src/node/examples$ curl http://npmjs.org/install.sh | sh
...
npm ok
It worked
coder@apollo:~/Work/src/node/examples$ npm search | wc -l
1918
coder@apollo:~/Work/src/node/examples$ npm install connect socket.io underscore
coder@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
- 50. server/
routing
progress_server.js
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})
)
- 51. server/
routing
progress_server.js
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("OKn")
job.stdout.on("data", function(output) {
var progress = parseInt(output.toString(), 10)
_(connections).each(function(connection) {
connection.send({"id": id, "progress": progress})
})
})
})
- 52. server/
socket.io
progress_server.js
io.listen(server).on("connection", function(client) {
connections[client.sessionId] = client
client.on("disconnect", function() {
delete connections[client.sessionId]
})
})
- 53. server/
simulation
progress_server.js
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()
})
- 54. interface/html
<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>
index.html
</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>
- 55. interface/
socket.io
<html>
...
<script>
$(function() {
var socket = new io.Socket()
index.html
socket.on("connect", function() { })
socket.on("disconnect", function() { })
socket.on("message", function(job) {
$("#template").progressBar(job.id, job.progress)
})
socket.connect()
})
</script>
</html>
- 56. interface/
progress bar
$.fn.progressBar = function(id, progress) {
var $template = $(this)
$("#job-" + id)
.otherwise(function() {
return $template.children().clone()
progress.js
.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()
})
})
}
- 57. interface/
progress bar
$.fn.otherwise = function(ifNotFound) {
if (this.length === 0) {
return ifNotFound()
}
progress.js
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
}
- 59. server/
event emitter
var Job = (function() {
var EventEmitter = require("events").EventEmitter,
progress_server.js
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
})()
- 60. server/
event emitter
progress_server.js
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("OKn")
})
- 62. when to use it?
• chat/messaging
• real-time applications
• intelligent proxies
• high concurrency applications
• communication hubs
• coordinators
- 64. 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
- 69. gabriele lana
gabriele.lana@cleancode.it
twitter: @gabrielelana
http://joind.in/3359