Tag

misultin

3 articles
ausgegrenzt

A comparison between Misultin, Mochiweb, Cowboy, NodeJS and Tornadoweb

As some of you already know, I'm the author of Misultin, an Erlang HTTP lightweight server library. I'm interested in HTTP servers, I spend quite some time trying them out and am always interested in comparing them from different perspectives.

Today I wanted to try the same benchmark against various HTTP server libraries:

I've chosen these libraries because they are the ones which currently interest me the most. Misultin, obviously since I wrote it; Mochiweb, since it's a very solid library widely used in production (afaik it has been used or is still used to empower the Facebook Chat, amongst other things); Cowboy, a newly born lib whose programmer is very active in the Erlang community; NodeJS, since bringing javascript to the backend has opened up a new whole world of possibilities (code reusable in frontend, ease of access to various programmers,...); and finally, Tornadoweb, since Python still remains one of my favourites languages out there, and Tornadoweb has been excelling in loads of benchmarks and in production, empowering FriendFeed.

Two main ideas are behind this benchmark. First, I did not want to do a "Hello World" kind of test: we have static servers such as Nginx that wonderfully perform in such tasks. This benchmark needed to address dynamic servers. Second, I wanted sockets to get periodically closed down, since having all the load on a few sockets scarcely correspond to real life situations.

For the latter reason, I decided to use a patched version of HttPerf. It's a widely known and used benchmark tool from HP, which basically tries to send a desired number of requests out to a server and reports how many of these actually got replied, and how many errors were experienced in the process (together with a variety of other pieces of information). A great thing about HttPerf is that you can set a parameter, called --num-calls, which sets the amount of calls per session (i.e. socket connection) before the socket gets closed by the client. The command issued in these tests was:

httperf --timeout=5 --client=0/1 --server= --port=8080 --uri=/?value=benchmarks --rate= --send-buffer=4096
        --recv-buffer=16384 --num-conns=5000 --num-calls=10

The value of rate has been set incrementally between 100 and 1,200. Since the number of requests/sec = rate * num-calls, the tests were conducted for a desired number of responses/sec incrementing from 1,000 to 12,000. The total number of requests = num-conns * rate, which has therefore been a fixed value of 50,000 along every test iteration.

The test basically asks servers to:

  • check if a GET variable is set
  • if the variable is not set, reply with an XML stating the error
  • if the variable is set, echo it inside an XML

Therefore, what is being tested is:

  • headers parsing
  • querystring parsing
  • string concatenation
  • sockets implementation

The server is a virtualized up-to-date Ubuntu 10.04 LTS with 2 CPU and 1.5GB of RAM. Its /etc/sysctl.conf file has been tuned with these parameters:

BASH
# Maximum TCP Receive Window
net.core.rmem_max = 33554432
# Maximum TCP Send Window
net.core.wmem_max = 33554432
# others
net.ipv4.tcp_rmem = 4096 16384 33554432
net.ipv4.tcp_wmem = 4096 16384 33554432
net.ipv4.tcp_syncookies = 1
# this gives the kernel more memory for tcp which you need with many (100k+) open socket connections
net.ipv4.tcp_mem = 786432 1048576 26777216
net.ipv4.tcp_max_tw_buckets = 360000
net.core.netdev_max_backlog = 2500
vm.min_free_kbytes = 65536
vm.swappiness = 0
net.ipv4.ip_local_port_range = 1024 65535
net.core.somaxconn = 65535

The /etc/security/limits.conf file has been tuned so that ulimit -n is set to 65535 for both hard and soft limits.

Here is the code for the different servers.

Misultin

ERLANG
-module(misultin_bench).
-export([start/1, stop/0, handle_http/1]).

start(Port) ->
    misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).

stop() ->
    misultin:stop().

handle_http(Req) ->
    % get value parameter
    Args = Req:parse_qs(),
    Value = misultin_utility:get_key_value("value", Args),
    case Value of
        undefined ->
            Req:ok([{"Content-Type", "text/xml"}], ["no value specified"]);
        _ ->
            Req:ok([{"Content-Type", "text/xml"}], ["", Value, ""])
    end.

Mochiweb

ERLANG
-module(mochi_bench).
-export([start/1, stop/0, handle_http/1]).

start(Port) ->
    mochiweb_http:start([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).

stop() ->
    mochiweb_http:stop().

handle_http(Req) ->
    % get value parameter
    Args = Req:parse_qs(),
    Value = misultin_utility:get_key_value("value", Args),
    case Value of
        undefined ->
            Req:respond({200, [{"Content-Type", "text/xml"}], ["no value specified"]});
        _ ->
            Req:respond({200, [{"Content-Type", "text/xml"}], ["", Value, ""]})
    end.

Note: i'm using misultin_utility:get_key_value/2 function inside this code since proplists:get_value/2 is much slower.

Cowboy

ERLANG
-module(cowboy_bench).
-export([start/1, stop/0]).

start(Port) ->
    application:start(cowboy),
    Dispatch = [
        %% {Host, list({Path, Handler, Opts})}
        {'_', [{'_', cowboy_bench_handler, []}]}
    ],
    %% Name, NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
    cowboy:start_listener(http, 100,
        cowboy_tcp_transport, [{port, Port}],
        cowboy_http_protocol, [{dispatch, Dispatch}]
    ).

stop() ->
    application:stop(cowboy).
ERLANG
-module(cowboy_bench_handler).
-behaviour(cowboy_http_handler).
-export([init/3, handle/2, terminate/2]).

init({tcp, http}, Req, _Opts) ->
    {ok, Req, undefined_state}.

handle(Req, State) ->
    {ok, Req2} = case cowboy_http_req:qs_val(<<"value">>, Req) of
        {undefined, _} ->
            cowboy_http_req:reply(200, [{<<"Content-Type">>, <<"text/xml">>}], <<"no value specified">>, Req);
        {Value, _} ->
            cowboy_http_req:reply(200, [{<<"Content-Type">>, <<"text/xml">>}], ["", Value, ""], Req)
    end,
    {ok, Req2, State}.

terminate(_Req, _State) ->
    ok.

NodeJS

JAVASCRIPT
var http = require('http'), url = require('url');
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type":"text/xml"});
    var urlObj = url.parse(request.url, true);
    var value = urlObj.query["value"];
    if (value == ''){
        response.end("no value specified");
    } else {
        response.end("" + value + "");
    }
}).listen(8080);

Tornadoweb

PYTHON
import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        value = self.get_argument('value', '')
        self.set_header('Content-Type', 'text/xml')
        if value == '':
            self.write("no value specified")
        else:
            self.write("" + value + "")

application = tornado.web.Application([
    (r"/", MainHandler),
])

if __name__ == "__main__":
    application.listen(8080)
    tornado.ioloop.IOLoop.instance().start()

I took this code and run it against:

  • Misultin 0.7.1 (Erlang R14B02)
  • Mochiweb 1.5.2 (Erlang R14B02)
  • Cowboy master 420f5ba (Erlang R14B02)
  • NodeJS 0.4.7
  • Tornadoweb 1.2.1 (Python 2.6.5)

All the libraries have been run with the standard settings. Erlang was launched with Kernel Polling enabled, and with SMP disabled so that a single CPU was used by all the libraries.

Test results

The raw printout of HttPerf results that I got can be downloaded from here.

Note: the above graph has a logarithmic Y scale.

According to this, we see that Tornadoweb tops at around 1,500 responses/seconds, NodeJS at 3,000, Mochiweb at 4,850, Cowboy at 8,600 and Misultin at 9,700. While Misultin and Cowboy experience very little or no error at all, the other servers seem to funnel under the load. Please note that "Errors" are timeout errors (over 5 seconds without a reply). Total responses and response times speak for themselves.

I have to say that I'm surprised on these results, to the point I'd like to have feedback on code and methodology, with alternate tests that can be performed. Any input is welcome, and I'm available to update this post and correct eventual errors I've made, as an ongoing discussion with whomever wants to contribute.

However, please do refrain from flame wars which are not welcomed here. I have published this post exactly because I was surprised on the results I got.

What is your opinion on all this?

-----------------------------------------------------

UPDATE (May 16th, 2011)

Due to the success of these benchmarks I want to stress an important point when you read any of these (including mines).

Benchmarks often are misleading interpreted as "the higher you are on a graph, the best that *lib-of-the-moment-name-here* is at doing everything". This is absolutely the wrongest way to look at those. I cannot stress this point enough.

'Fast' is only 1 of the 'n' features you desire from a webserver library: you definitely want to consider stability, features, ease of maintenance, low standard deviation, code usability, community, developments speed, and many other factors whenever choosing the best suited library for your own application. There is no such thing as generic benchmarks. These ones are related to a very specific situation: fast application computational times, loads of connections, and small data transfer.

Therefore, please use this with a grain of salt and do not jump to generic conclusions regarding any of the cited libraries, which as I've clearly stated in the beginning of my post I all find interesting and valuable. And I still am very open in being criticized for the described methodology or other things I might have missed.

Continue Reading…
misultin

Misultin: erlang and websockets

Inspired by Joe Armstrong's post, I've recently added websocket support to misultin v0.4, my Erlang library for building fast lightweight HTTP servers.

Basically, websockets allow a two-way asynchronous communication between browser and servers, filling the gap that some technologies such as ajax and comet have tried to fulfill in these recent years. If you want to try this out yourself, you will first need to grab a browser which implements websockets, such as Google Chrome.

The typical html page with javascript code to use websockets is as follows:

JAVASCRIPT
<html>
   <head>
     <script type="text/javascript">
         function addStatus(text){
            var date = new Date();
            document.getElementById('status').innerHTML = document.getElementById('status').innerHTML
               + date + ": " + text + "<br>";
         }
         function ready(){
            if ("WebSocket" in window) {
               // browser supports websockets
               var ws = new WebSocket("ws://localhost:8080/service");
               ws.onopen = function() {
                  // websocket is connected
                  addStatus("websocket connected!");
                  // send hello data to server.
                  ws.send("hello server!");
                  addStatus("sent message to server: 'hello server'!");
               };
               ws.onmessage = function (evt) {
                  var receivedMsg = evt.data;
                  addStatus("server sent the following: '" + receivedMsg + "'");
               };
               ws.onclose = function() {
                  // websocket was closed
                  addStatus("websocket was closed");
               };
            } else {
               // browser does not support websockets
               addStatus("sorry, your browser does not support websockets.");
            }
         }
      </script>
   </head>
   <body onload="ready();">
      <div id="status"></div>
   </body>
</html>

Here’s the code to use misultin to handle the requests of this script:

-module(misultin_websocket_example).
-export([start/1, stop/0]).

% start misultin http server
start(Port) ->
    misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end},
    {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]).

% stop misultin
stop() ->
    misultin:stop().

% callback on request received
handle_http(Req, Port) ->
    % output
    Req:ok([]).

% callback on received websockets data
handle_websocket(Ws) ->
    receive
        {browser, Data} ->
            Ws:send(["received '", Data, "'"]),
            handle_websocket(Ws);
        _Ignore ->
            handle_websocket(Ws)
    after 5000 ->
        Ws:send("pushing!"),
        handle_websocket(Ws)
    end.

handle_websocket/1 is spawned by misultin to handle the connected websockets. Data coming from a browser will be sent to this process and will have the message format {browser, Data}, where Data is a string(). If you need to send data to the browser, you may do so by using the parametrized function Ws:send(Data), Data being a string() or an iolist().

Compile and run the example here above with misultin_websocket_example:start(8080). Then, open up your Chrome (or other websocket compliant browser) and point it to an .html file containing the above code.

You should normally see this being gradually printed on your browser:

Wed Jan 20 2010 15:18:52 GMT+0100 (CET): websocket connected!
Wed Jan 20 2010 15:18:52 GMT+0100 (CET): sent message to server: 'hello server'!
Wed Jan 20 2010 15:18:52 GMT+0100 (CET): server sent the following: 'received 'hello server!''
Wed Jan 20 2010 15:18:57 GMT+0100 (CET): server sent the following: 'pushing!'
Wed Jan 20 2010 15:19:02 GMT+0100 (CET): server sent the following: 'pushing!'
Wed Jan 20 2010 15:19:07 GMT+0100 (CET): server sent the following: 'pushing!'

In normal environments you may consider serving the .html page from misultin directly. You may do so with the following and complete misultin module:

ERLANG
-module(misultin_websocket_example).
-export([start/1, stop/0]).

% start misultin http server
start(Port) ->
   misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req, Port) end}, {ws_loop, fun(Ws) -> handle_websocket(Ws) end}]).

% stop misultin
stop() ->
   misultin:stop().

% callback on request received
handle_http(Req, Port) ->
   % output
   Req:ok([{"Content-Type", "text/html"}],
   ["
   <html>
      <head>
         <script type=\"text/javascript\">
            function addStatus(text){
               var date = new Date();
               document.getElementById('status').innerHTML = document.getElementById('status').innerHTML
                  + date + \": \" + text + \"<br>\";
            }
            function ready(){
               if (\"WebSocket\" in window) {
                  // browser supports websockets
                  var ws = new WebSocket(\"ws://localhost:", integer_to_list(Port) ,"/service\");
                  ws.onopen = function() {
                     // websocket is connected
                     addStatus(\"websocket connected!\");
                     // send hello data to server.
                     ws.send(\"hello server!\");
                     addStatus(\"sent message to server: 'hello server'!\");
                  };
                  ws.onmessage = function (evt) {
                     var receivedMsg = evt.data;
                     addStatus(\"server sent the following: '\" + receivedMsg + \"'\");
                  };
                  ws.onclose = function() {
                     // websocket was closed
                     addStatus(\"websocket was closed\");
                  };
               } else {
                  // browser does not support websockets
                  addStatus(\"sorry, your browser does not support websockets.\");
               }
            }
         </script>
      </head>
      <body onload=\"ready();\">
         <div id=\"status\"></div>
      </body>
   </html>"]).

% callback on received websockets data
handle_websocket(Ws) ->
   receive
      {browser, Data} ->
         Ws:send(["received '", Data, "'"]),
         handle_websocket(Ws);
      _Ignore ->
         handle_websocket(Ws)
   after 5000 ->
      Ws:send("pushing!"),
      handle_websocket(Ws)
   end.

Please note that the Websocket Protocol still is draft. use with caution.

Continue Reading…
misultin

Misultin library

Today I've released Misultin (pronounced mee-sul-teen), an Erlang library for building fast lightweight HTTP servers. The first benchmarks are quite satisfying, even though there still is work to do.

Here is the simple code for Misultin's Hello World.

ERLANG
-module(misultin_hello_world).
-vsn('0.1').
-export([start/1, stop/0, handle_http/1]).

% start misultin http server
start(Port) ->
    misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).

% stop misultin
stop() ->
    misultin:stop().

% callback on request received
handle_http(Req) ->
    Req:ok("Hello World.").

Here's the code to echo a GET variable in a XML form.

ERLANG
-module(misultin_get_variable).
-vsn('0.1').
-export([start/1, stop/0, handle_http/1]).

% start misultin http server
start(Port) ->
    misultin:start_link([{port, Port}, {loop, fun(Req) -> handle_http(Req) end}]).

% stop misultin
stop() ->
    misultin:stop().

% callback on request received
handle_http(Req) ->
    % get params
    Args = Req:parse_qs(),
    Value = proplists:get_value("value", Args),
    case Value of
        undefined ->
            Req:ok([{"Content-Type", "text/xml"}], "no value specified");
        _ ->
            Req:ok([{"Content-Type", "text/xml"}], "~s", [Value])
    end.

Available also are additional code examples, and the full list of exports.

You may find Misultin, released under the New BSD License, on its project page on Google code.

Continue Reading…