Chrome JS engine: V8 (en cómic)
Adapted for the server by Ryan Dahl
Free software (know your software)
Supported by Joyent
Asynchronous
Non-blocking
Event-driven
Similar servers:
Ruby: EventMachine
Java: Akka
Source: nodejs.org/about, Wikipedia
A programmer had a problem.
He thought to himself,
"I know, I'll solve it with threads!".
has Now problems. two he
Source: Davidlohr Bueso
Threads: Apache MTM, Java Servlets
Events: nginx, node.js
But is it really fast?
Not when running sequential code
... but it is in concurrent execution
Only one running thread (essentially threadless)
Multi-process by using the cluster module
Node.js is highly scalable
Node.js is very linear
Account Overview page
2 engineers for 4 months
Developed twice as fast as the Java version
33% less code, 40% less files
Supports twice as many requests per second
Response times 35% faster
Source: Node.js at PayPal
Initial platform: one engineer for a year
Current platform: three engineers for another year
Serves mobile ads for performance campaigns
Supports 20K requests/second (at least)
Scales to 30 servers (that we know of)
Latency < 80 ms
Source: internal data
Create node.js project
Add to GitHub
'Ello world!
Socket server, port 1702
When a connection is opened, asks: "Hello?"
After receiving "hello" it answers "world" and ends
Any other message should result in "ERROR"
Technical note: careful with your carriage returns!
Always "\r\n".
Doc: node.js net module
var net = require('net');
var server = net.createServer(function(connection) {
console.log('Connection open');
connection.write('Hello?');
connection.on('data', function(data) {
if (String(data).trim() != 'hello') {
connection.write('ERROR');
} else {
connection.write('world');
console.log('Connection closed');
}
});
});
server.listen(port);
Created in 2003 by Brad Fitzpatrick for LiveJournal
NoSQL before NoSQL was cool
Key-value
In-memory only storage
Free software of course!
Storage for precomputed values
Almost instantaneous responses (~10 µs)
set [key] [flags] [exptime] [bytes]\r\n
[data block]\r\n
\=> STORED
\=> NOT_STORED
store a value in memory
get [key]* \r\nextract one (or more) values by key
\=> VALUE [key] [flags] [bytes]\r\n
[data block]\r\n
END\r\n
delete [key]delete a value from memory
\=> DELETED
\=> NOT_FOUND
Source: protocol.txt
get, gets
set, add, replace, append, prepend, cas
delete
incr, decr,
touch
slabs
stats
flush_all, version, quit
Source: protocol.txt
Simplified memcached server
get, set, delete
Simplified protocol
Only accepts strings (without line feeds)
Memcached-type server on port 11311
set [key] [value]\r\n
\=> STORED
\=> NOT_STORED
store a value in memory
get [key]\r\nextract a value by key
\=> VALUE [key] [value]\r\n
\=> END\r\n
delete [key]\r\ndelete a value from memory
\=> DELETED
\=> NOT_FOUND
string.trim()
: remove whitespace at the ends
string.split(separator)
: split a string into an array of pieces
array.slice(begin)
: return a substring from begin
array.join(separator)
: join an array into a string
A JavaScript object is an associative map by key
The value can contain spaces
Careful with error cases
Add a quit command:
quit\r\n
closes the client
Hint: connection.end()
: closes a connection
Verify how easy it is to modify your software!
Probably not
Read me file: README.md
Definition: package.json
Entry point: index.js
Code: lib/
Documentation: doc/
Binaries: bin/
npm install
: install a package or morenpm install -g
: global installationnpm update
: update one or more packagesnpm remove
: remove a packagenpm test
: run package testsSource: npm-index
Local:
node_modules
../node_modules
../../node_modules
...
$HOME/node_modules
./node_modules
Global:
{prefix}/lib/node_modules
where {prefix} is usually /usr/local
Source: npm-folders
{
"name": "simplecached",
"version": "0.0.1",
"description": "Simplified memcached server.",
"contributors": ["Alex Fernández <alexfernandeznpm@gmail.com>"],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/alexfernandez/simplecached"
},
"dependencies": {
"testing": "0.1.x"
},
"keywords" : ["simplecached", "didactic", "memcached", "caching"],
"bin": {
"simplecached": "bin/server.js"
},
"scripts": {
"test": "node test.js"
},
}
Source: package.json
We will use the simplecached package later on
Install it:
npm install simplecached
Imported with require:
var Client = require('simplecached').Client; var client = new Client(port); client.set(key, value, function(error) {}); client.get(key, function(error, value) {});
Source: simplecached
Name and description
Depends on simplecached
npm install
without argumentsnpm list
to see what you have installednpm remove simplecached
npm list
npm publish
Take it back! with npm unpublish --force
Customers are happy when your code works
Untested code does not work!
Trust me on this
Do not ever sell, announce or deploy untested code
Do not talk about tests separately from code
Use testing
Build your async tests
Create a test function for every unit test
Run all tests together
testing.success(message, callback);
consider that the test succeeded
testing.failure(message, callback);
consider that the test failed
testing.assert(condition, message, callback);
verify a condition, fail if not true
testing.run(tests, testing.show)
run a set of tests and show the results
Let the machine work for you
Create a control API
Use it in your tests
Three basic rules:
The simplecached package contains a client. Use it!
Installation:
npm install simplecached
Imported with require:
var Client = require('simplecached').Client; var client = new Client(port); client.set(key, value, function(error) {}); client.get(key, function(error, value) {});
Source: simplecached
Functional programming concept
CPS: Continuation-passing Style
State is passed around as parameters
Ideal for asynchronous calls
Pyramid of callbacks
Infinite nested continuations
Abuse of lambdas (unnamed functions)
Hard to follow, debug, refactor
Use named functions
Create an object with named functions
Closures become attributes
Use EventEmitter
Coroutines (and not goroutines): not yet there
Refactor the pyramid of callbacks
Do not keep more than two nested callbacks
Choose your poisonmethod