Connect - Middleware For NodeJS

Yesterday myself and Tim Caswell open-sourced the first Ext JS nodejs project, Connect. Connect is an abstraction layer, providing node developers with an effective, high performance solution for rapid development using interchangable components called “middleware”.

Middleware

Middleware provides node developers with simple “plug and play” modules, which may be stacked in any order desired, aiding in rapid development. Connect middlware are regular node modules exporting the handle() method, however conceptually they fall into two categories, filters, and providers.

A “filter” conceptually sits arbitrarily within the middlware stack, processing incoming and outgoing traffic, but typically does not respond to a request. An example of this is the log filter provided by Connect, it does not respond to any request, it simply proxies function calls in order to log request data.

A “provider” differs conceptually in the fact that it provides an end-point in the stack. However this may not always be the case. For example a json-rpc provider may choose not to process or respond to a request if the Content-Type header is not application/json. A classical example of a provider is the static provider which serves static files.

Executable

Connect is a dual purpose library, rapid development is not the only goal in mind. Also provided is the connect executable which can be used to daemonize Connect servers (and regular node servers) using an extremely simple command-line interface.

For more information on the connect executable install Connect and run:

$ man connect

Performance

Performance is a top priority for Connect, and we have the benchmarks to prove it. The following benchmarks were performed with ApacheBench, node 0.1.97, thin 1.2.7, sinatra 1.0, and rack 1.1.0.

The benchmarks bundled with Connect consistently showed that the library has nearly no overhead compared to a regular node server when responding with the typical “Hello World” response.

Hello World

Next up we have node, sinatra (thin), and Connect serving jquery.js, a roughly ~57kb.

Static jQuery

If you wish to run the benchmarks on your own machine, first execute:

$ make benchmark

Then generate the graphs with gnuplot:

$ make graphs

Once complete:

$ open results/graphs

Conclusion

Connect is awesome, what are you waiting for! head over to the Github repo and fork away.

Express vs Sinatra Benchmarks

So I have been setting up benchmark scripts for Express today, and so far some of the results have been quite interesting! The numbers shown should be taken lightly, however they consistently show that Express is quite fast.

If you are interested in benchmarking your own web applications you might want to read my last article ApacheBench Gnuplot Graphing Benchmarks.

All of the following benchmarks were generated using ApacheBench with a concurrency of 50 and performs 2000 requests. Keep in mind that Thin is used to serve Sinatra requests.

Express vs Sinatra

For those who dont know Express is a NodeJS framework inspired by Ruby’s Sinatra. Below we have the benchmark results for a typical “Hello World” response, which include nodejs benchmarks without the overhead of features provided by Express:

express vs sinatraexpress sinatra requests per second

Haml.js vs Ruby Haml

Next up is my JavaScript Haml implementation. Below are the results of running Express & haml.js vs Sinatra & Haml. Both serve a layout template, as well as a page specific template.

javascript haml vs ruby hamlrequests per second

Sass.js vs Ruby Sass

Finally we have JavaScript Sass vs the regular Ruby implementation packaged with haml. Both implementations serve the same 80 line stylesheet.

sass js vs ruby sasssass benchmarks

Stay tuned for more benchmarks!

ApacheBench GnuPlot Graphing Benchmarks

In this post I am going to be showing you how you can get set up creating http benchmark graphs using Gnuplot and ApacheBench. The ApacheBench or ab executable is packaged with Apache’s httpd, however it can be installed stand-alone as well from Google Code.

The Application

First things first, below we have an insanely simple NodeJS application, that will respond with the string Hello World, nothing fancy.

var sys = require('sys'),
  http = require('http');

var body = 'Hello World'
http.createServer(function(request, response) {
  response.writeHead(200, {
    'Content-Type': 'text/plain',
    'Content-Length': body.length
  });
  response.end(body, 'ascii');
}).listen(8000);

sys.puts('Server running at http://127.0.0.1:8000/')

Benchmarking

There are several ways to customize ApacheBench, however for this post we are going to keep things simple and simply hit the server with 8000 requests, with a concurrency of 100:

$  ab -n 8000 -c 100 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 800 requests
Completed 1600 requests
Completed 2400 requests
Completed 3200 requests
Completed 4000 requests
Completed 4800 requests
Completed 5600 requests
Completed 6400 requests
Completed 7200 requests
Completed 8000 requests
Finished 8000 requests


Server Software:        
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   1.340 seconds
Complete requests:      8000
Failed requests:        0
Write errors:           0
Total transferred:      760285 bytes
HTML transferred:       88033 bytes
Requests per second:    5970.16 [#/sec] (mean)
Time per request:       16.750 [ms] (mean)
Time per request:       0.167 [ms] (mean, across all concurrent requests)
Transfer rate:          554.08 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3   1.7      3       9
Processing:     4   14   3.7     13      30
Waiting:        3   12   4.0     12      29
Total:          8   17   2.5     16      30

Percentage of the requests served within a certain time (ms)
  50%     16
  66%     17
  75%     18
  80%     18
  90%     20
  95%     21
  98%     23
  99%     25
 100%     30 (longest request)

Benchmarks like these are not overly practical but can provide insight to how well a server can respond when being hit with the same request. The results above will certainly vary depending on your system, the resources available, etc.

Graphing

Now although the report generated is helpful, we need to output the data in a parse-able format. To do this we use the -g switch which will output a gnuplot-friendly tab delimited table of data. Note that -e is available and will output CSV data for those of you wishing to generate graphs in programs like excel (fuck! microsoft sucks).

Run your benchmarks again to generate out.dat (note the filename is not important, out.txt etc is fine):

$ ab -n 8000 -c 100 -g out.dat http://127.0.0.1:8000/

We can now either generate graphs using gnuplot’s shell, or we can create a script that we can re-use to generate the graph. When playing around with customizing the graph, I would recommend using the shell which can be started by invoking gnuplot with no args:

$ gnuplot
gnuplot> set title "My Benchmark"
gnuplot> plot "out.dat" using 9

Or as I mentioned we can use a plot script which can be re-used easily. Mine typically looks something like the annotated version below (saved as plot.p):

# output as png image
set terminal png

# save file to "out.png"
set output "out.png"

# graph title
set title "ab -n 8000 -c 100"

# nicer aspect ratio for image size
set size 1,0.7

# y-axis grid
set grid y

# x-axis label
set xlabel "request"

# y-axis label
set ylabel "response time (ms)"

# plot data from "out.dat" using column 9 with smooth sbezier lines
# and title of "nodejs" for the given data
plot "out.dat" using 9 smooth sbezier with lines title "nodejs"

And generate the graph with:

$ gnuplot plot.p

Apache bench gnuplot

Graphing Several Data Sets

Plotting several data-sets is helpful when comparing various servers etc, so lets change up our nodejs server a bit. Below we wrap the response with a 0-1000 millisecond setTimeout() to vary the results.

var sys = require('sys'),
  http = require('http');

var body = 'Hello World'
http.createServer(function(request, response) {
  setTimeout(function(){
    response.writeHead(200, { 'Content-Type': 'text/plain', 'Content-Length': body.length });
    response.end(body, 'ascii');
  }, Math.floor(Math.random() * 1000))
}).listen(8000);

sys.puts('Server running at http://127.0.0.1:8000/')

Now execute the same ab command, however change out.dat to out2.dat, and add a line to plot.p which tells gnuplot to use “out2.dat” for the second set of data.

plot "out.dat" using 9 smooth sbezier with lines title "nodejs", \
       "out2.dat" using 9 smooth sbezier with lines title "nodejs random timeout"

Now run the gnuplot command again to generate the png image:

multiple data sets

Automation

So far our data has been pretty static, however a bash script can easily be used to generate the plot script dynamically. A simple example of this can be seen in the Express benchmarks used to generate graphs against node, express, thin, etc.

Hope that helps some of you guys!