libuv - The Power Underneath Node.js

Brandon Philips
brandon@ifup.org
@brandonphilips
June 29th, 2012

Why I care about libuv

Orientation

Major topics

Event driven non-blocking I/O

Basic idea

Event loop pseudo code

while (1) {
  nfds = poll(fds, next_timer());

  if (nfds == 0)
     timer_callback();

  for(n = 0; n < nfds; ++n) {
     if (fds[n] == READY)
        callbacks[n]();
  }
}

Strategy Comparison time!

Event Driven I/O Loop

CPU bound work blocking everything example

function fib(n) {
  if (n < 2)
    return n;
  return fib(n-1) + fib(n-2)
}

var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(fib(40) + "\n");
}).listen(8080, '127.0.0.1');

console.log('Server running at http://127.0.0.1:8080/');

Threaded model

Process per request

Conclusions

Back to libuv

The Short History of libuv

Big list of features

Windows, Linux, OSX, BSD

Lets dive into some code

https://github.com/philips/libuv-webserver

Main

  parser_settings.on_headers_complete = on_headers_complete;

  resbuf.base = RESPONSE;
  resbuf.len = sizeof(RESPONSE);

  uv_loop = uv_default_loop();

  uv_tcp_init(uv_loop, &server);

  struct sockaddr_in address = uv_ip4_addr("0.0.0.0", 8000);

  uv_tcp_bind(&server, address);

  uv_listen((uv_stream_t*)&server, 128, on_connect);

  printf("listening on port 8000\n");

  uv_run(uv_loop);

on_connect

  client_t* client = malloc(sizeof(client_t));
  client->request_num = request_num;

  uv_tcp_init(uv_loop, &client->handle);
  http_parser_init(&client->parser, HTTP_REQUEST);

  client->parser.data = client;
  client->handle.data = client;

  uv_accept(server_handle, (uv_stream_t*)&client->handle);

  uv_read_start((uv_stream_t*)&client->handle, on_alloc, on_read);

on_read

  size_t parsed;

  client_t* client = (client_t*) tcp->data;

  if (nread >= 0) {
    parsed = http_parser_execute(
        &client->parser, &parser_settings, buf.base, nread);
    if (parsed < nread) {
      LOG_ERROR("parse error");
      uv_close((uv_handle_t*) &client->handle, on_close);
    }
  } else {
    uv_err_t err = uv_last_error(uv_loop);
    if (err.code != UV_EOF) {
      UVERR(err, "read");
    }
  }

  free(buf.base);

on_headers_complete

  client_t* client = (client_t*) parser->data;

  uv_write(
      &client->write_req,
      (uv_stream_t*)&client->handle,
      &resbuf,
      1,
      after_write);

  return 1;

How does it perform

How to save that trouble

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(8000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:8000/');

Asyncing POSIX

Async FS and DNS

Building a platform with libuv

The basics

What magic is in node that isn’t libuv

Trouble spots luvit has hit

Keeping refs through a callback

Ref counting is a bit odd

Having to bind to openssl

Conclusions

Projects using libuv

Thanks! - contact me: brandon@ifup.org ifup.org/slides

Source: Octodex

http://cconf.org

http://rackertalent.com/sanfrancisco/