Node
Node is a purely evented I/O framework for V8 javascript. For example, this is a simple web server which responds with "Hello World" after waiting two seconds:
new node.http.Server(function (msg) {
setTimeout(function () {
msg.sendHeader(200, [["Content-Type", "text/plain"]]);
msg.sendBody("Hello World");
msg.finish();
}, 2000);
}).listen(8000, "localhost");
This script can handle hundreds of concurrent requests while using little CPU or memory—see benchmarks. Check out the documentation for more examples.
Node is free to download, use, and build upon.
Motivation
- Evented programming makes sense
- difference between blocking/non-blocking design
There are many methods to write internet servers but they can fundamentally be divided into two camps: evented and threaded; non-blocking and blocking. A blocking server accepts a connection and launches a new thread to handle the connection. Because the concurrency is handled by the thread scheduler, a blocking server can make function calls which preform full network requests.
var response = db.execute("SELECT * FROM table"); // do somethingAn evented server manages its concurrency itself. All connections are handled in a single thread and callbacks are executed on certain events: "socket 23 is has data to read", "socket 65's write buffer is empty". An evented server executes small bits of code but never blocks the process. In the evented world callbacks are used instead of functions
db.execute("SELECT * FROM table", function (response) { // do something }); - I/O latency
l1 cache ~ 3 l2 cache ~ 14 ram ~ 250 disk ~ 41000000 network ~ 240000000 - purely evented interfaces rule out a lot of stupidity
- difference between blocking/non-blocking design
- Evented programs are more efficient
- pthread stack size 2mb default stack size on linux (1mb on windows, 64kb on FreeBSD) of course this is adjustable
- context switching benchmark
- Apache vs. Nginx
- event machine vs mongrel (neverblock)
- The appropriateness of Javascript
- No I/O
Javascript is without I/O. In the browser the DOM provides I/O, but non-browser javascript interpreters have only non-standardized functions to allow them print to console or access the network.
- No Threads
- Good compiler
- Universality of the language
Contemporary computer infrastructure has two irreplaceable languages: C and Javascript. C is the language of operating systems. POSIX, the universal operating system API, is defined in C. So while you can interface with operating systems in Java and Haskell, those languages access must make system calls in C. Similarly, Javascript is the language of the web operating system. In place of POSIX is the DOM. You can wrap Javascript, you can compile to Javascript, but in the end browsers must be interfaced with in Javascript. Portable low-level systems tend to be written in C and portable web-level systems are written in Javascript.
- No I/O
Benchmarks
TODO
Download
TODO
Build
configure make make install
Application Programming Interface
The node executable should be given an argument pointing to a javascript file.
Timers
The timer API is the same as in the browser. The functions
setTimeout, setInterval, clearTimeout, and clearInterval
File System
TCP
HTTP (node.http)
Node provides a fast web server and client interface. The interface is
rather low-level. Node, for example, will not parse
application/x-www-form-urlencoded message bodies—that is
for higher level code to manage. The interface does abstract the
Transfer-Encoding (i.e. chuncked or identity), Message boundarys, and
Keep-Alive connections.
HTTP Server (node.http.Server)
new Server(request_handler, options)-
Creates a new web server. The
optionsargument accepts the same values as the options argument fornode.tcp.Serverdoes. The options argument is optional.The
request_handleris a function which is called on each request with aMessageobject argument. server.listen(port, hostname)-
Begin accepting connections on the specified port and hostname. If the hostname is omitted, the server will accept connections directed to any address.
server.close()-
Stops the server. Requests currently in progress will not be interrupted.
HTTP Request Message (node.http.Message)
This object is only created internally—not by the user. It is passed
as an argument to the request_handler function in a web server.
msg.method- The request method as a string. Read only. Example:
"GET","DELETE". msg.uri- The request URI as a string. Read only.
Example:
"/index.html?hello=world". msg.headers- The request headers expressed as an array of 2-element arrays. Read only.
Example:
[ ["Content-Length", "123"] , ["Content-Type", "text/plain"] , ["Connection", "keep-alive"] , ["Accept", "*/*"] ]
msg.http_version- The HTTP protocol version as a string. Read only. Examples:
"1.1","1.0" msg.connection- A reference to the
node.tcp.Connectionobject. Read only. Note that multiple messages can be sent on a single connection. msg.onBody- Callback. Should be set by the user to be informed of when a piece
of the message body is received. Example:
msg.onBody = function (chunk) { puts("part of the body: " + chunk); }A chunk of the body is given as the single argument. The transfer-encoding has been removed.The body chunk is either a String in the case of utf8 encoding or an array of numbers in the case of raw encoding.
msg.onBodyComplete- Callback. Made exactly once for each message. No arguments. After
onBodyCompleteis executedonBodywill no longer be called. msg.setBodyEncoding(encoding)-
Set the encoding for the request body. Either
"utf8"or"raw". Defaults to raw. TODO msg.sendHeader(status_code, headers)-
Sends a response header to the request. The status code is a 3-digit
HTTP status code, like
404. The second argument,headers, should be an array of 2-element arrays, representing the response headers.Example:
var body = "hello world"; msg.sendHeader( 200 , [ ["Content-Length", body.length] , ["Content-Type", "text/plain"] ] );This method must only be called once on a message and it must be called beforemsg.finish()is called. msg.sendBody(chunk)-
This method must be called after
sendHeaderwas called. It sends a chunk of the response body. This method may be called multiple times to provide successive parts of the body. msg.finish()-
This method signals that all of the response headers and body has been
sent; that server should consider this message complete.
The method,
msg.finish(), MUST be called on each response.
Modules
Node has simple module loading. Here is an example of loading a module:
include("mjsunit");
function onLoad () {
assertEquals(1, 2);
}
Here the module mjsunit has provided the function
assertEquals().
The file mjsunit.js must be in the same directory for this
to work. The include() function will take all the exported
objects from the file and put them into the global namespace. Because file
loading does not happen instantaniously, and because Node has a policy of
never blocking, the callback onLoad() is provided to notify the
user when all the exported functions are completely loaded.
To export an object, add to the special object exports.
Let's look at how mjsunit.js does this
function fail (expected, found, name_opt) {
// ...
}
function deepEquals (a, b) {
// ...
}
exports.assertEquals = function (expected, found, name_opt) {
if (!deepEquals(found, expected)) {
fail(expected, found, name_opt);
}
};
The functions fail and deepEquals are not
exported and remain private to the module.
In addition to include() a module can use
require(). Instead of loading the exported objects into the
global namespace, it will return a namespace object. Again, the members of
the namespace object can only be guarenteed to exist after the
onLoad() callback is made. For example:
var mjsunit = require("mjsunit");
function onLoad () {
mjsunit.assertEquals(1, 2);
}