billywhizz
2/14/2012 - 11:50 PM

node.js static file serving benchmark

node.js static file serving benchmark

ab -k -c 20 -n 50000 http://127.0.0.1/0k.txt

Server Software:        node-/0.6.10
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /0k.txt
Document Length:        0 bytes

Concurrency Level:      20
Time taken for tests:   1.821 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    50000
Total transferred:      10700000 bytes
HTML transferred:       0 bytes
Requests per second:    27450.96 [#/sec] (mean)
Time per request:       0.729 [ms] (mean)
Time per request:       0.036 [ms] (mean, across all concurrent requests)
Transfer rate:          5736.82 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    1   0.1      1       4
Waiting:        0    1   0.1      1       4
Total:          1    1   0.1      1       4

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      1
 100%      4 (longest request)

ab -k -c 20 -n 50000 http://127.0.0.1:82/0k.txt

Server Software:        nginx/0.7.67
Server Hostname:        127.0.0.1
Server Port:            82

Document Path:          /0k.txt
Document Length:        0 bytes

Concurrency Level:      20
Time taken for tests:   1.052 seconds
Complete requests:      50000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    50000
Total transferred:      10750000 bytes
HTML transferred:       0 bytes
Requests per second:    47543.16 [#/sec] (mean)
Time per request:       0.421 [ms] (mean)
Time per request:       0.021 [ms] (mean, across all concurrent requests)
Transfer rate:          9982.21 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    0   0.2      0       1
Waiting:        0    0   0.2      0       1
Total:          0    0   0.2      0       1

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      1
 100%      1 (longest request)
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
  worker_connections 16384;
}
http {
  include /etc/nginx/mime.types;
  default_type application/octet-stream;
  access_log off;
  sendfile off;
  tcp_nopush on;
  tcp_nodelay on;
  open_file_cache max=1000 inactive=3600s;
  keepalive_timeout 65;
  keepalive_requests 100000;
  server {
    listen 82;
    server_name _;
    location / {
      root /tmp;
      index index.html;
    }
    error_page 404 /404.html;
    location = /404.html {
      root /var/www/html;
    }
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
      root /var/www/html;
    }
  }
}
var TCP = process.binding("tcp_wrap").TCP;
var Buffer = process.binding("buffer").SlowBuffer;
var HTTPParser = process.binding("http_parser").HTTPParser;

function setupSocket(peer) {
  function shutdownHandler(status, handle, req) {
    // TODO: ensure we only shutdown once
    if(status != 0) {
      if(peer.onerror) {
        var err = new Error("shutdown");
        err.errno = errno;
        peer.onerror(err);
      }
    }
    handle.close();
    if(peer.onclose) peer.onclose();
  }
  function kill() {
    if(!peer.closed) {
      peer.readStop();
      peer.onread = function() {};
      var r = peer.shutdown();
      peer.closed = true;
      r.oncomplete = shutdownHandler;
    }
  }
  function send(buff, cb) {
    var wr = peer.write(buff);
    if (!wr) {
      if(peer.onerror) {
        var err = new Error("write");
        err.errno = errno;
        peer.onerror(err);
      }
      peer.kill();
      return false;
    }
    wr.oncomplete = cb;
    return true;
  }
  peer.closed = false;
  peer.kill = kill;
  peer.send = send;
}

var responses = {
  keepalive: "HTTP/1.1 200 OK\r\nServer: node-/0.6.10\r\nDate: Thu, 09 Feb 2012 00:46:42 GMT\r\nContent-Type: text/html\r\nContent-Length: 0\r\nLast-Modified: Tue, 06 Dec 2011 17:29:28 GMT\r\nConnection: keep-alive\r\nAccept-Ranges: bytes\r\n\r\n",
  close: "HTTP/1.1 200 OK\r\nServer: nginx/0.7.67\r\nDate: Thu, 09 Feb 2012 00:46:42 GMT\r\nContent-Type: text/html\r\nContent-Length: 0\r\nLast-Modified: Tue, 06 Dec 2011 17:29:28 GMT\r\nConnection: close\r\nAccept-Ranges: bytes\r\n\r\n"
}

for (name in responses) {
  var b = new Buffer(responses[name].length);
  b.asciiWrite(responses[name]);
  responses[name] = b;
}

var FreeList = function(name, max, constructor) {
  this.name = name;
  this.constructor = constructor;
  this.max = max;
  this.list = [];
};

FreeList.prototype.alloc = function() {
  return this.list.length ? this.list.shift() : this.constructor.apply(this, arguments);
};

FreeList.prototype.free = function(obj) {
  if (this.list.length < this.max) this.list.push(obj);
};

var HTTPParsers = new FreeList("HTTPParsers", 1024, function() {
  var parser = new HTTPParser(HTTPParser.REQUEST);
  parser.onHeadersComplete = function(info) {
    var peer = this.peer;
    peer.info = info
  };
  parser.onMessageComplete = function() {
    var peer = this.peer;
    peer.send(peer.info.shouldKeepAlive?responses.keepalive:responses.close, function(status, handle, req) {
      if(status != 0) {
        var err = new Error("write");
        err.errno = errno;
        console.error(err);
        peer.kill();
        return;
      }
      if(!peer.info.shouldKeepAlive) peer.kill();
    });
  };
  return parser;
});

function onConnection(peer) {
  if(!peer) {
    var err = new Error("accept");
    err.errno = errno;
    console.error(err);
    return;
  }
  setupSocket(peer);
  var parser = HTTPParsers.alloc();
  parser.peer = peer;
  peer.onread = function(buf, start, len) {
    if(!buf) {
      var err = new Error("EOF");
      err.errno = errno;
      console.error(err);
      peer.kill();
      return;
    }
    var r = parser.execute(buf, start, len);
    if(r < 0) {
      var err = new Error("parse");
      console.error(err);
      peer.kill();
      return;
    }
  }
  peer.onerror = function(err) {
    console.error(err);
  }
  peer.onclose = function() {
    HTTPParsers.free(parser);
  }
  peer.readStart();
}

var sock = new TCP();
sock.bind("0.0.0.0", 80);
sock.onconnection = onConnection;
sock.listen();