Skip to content

Instantly share code, notes, and snippets.

@gurdiga
Last active December 27, 2015 06:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gurdiga/7282864 to your computer and use it in GitHub Desktop.
Save gurdiga/7282864 to your computer and use it in GitHub Desktop.

Web server comparison experiment: threaded Ruby vs. Node.js

Since a few weeks I’m working on a Node.js project at my day work, and I kind of loved the idea of the event loop, and then, because I just got off a Rails project, was wondering why wouldn’t that be possible with Ruby?

Googled a couple of evenings to find how Node.js manages to serve multiple requests on the same thread, and how its IO works. Then did the same for Ruby, read a bit about GIL, and found that in the end, there is not that much of a difference between what you can get from a threaded Ruby server and Node.js.

Played a bit with hello worlds and ended up with similar results from the speed and concurency standpoint: max 2 concurent requests on my 2CPU laptop.

For Ruby I spawn a separated Thread for every request. And from what I could google, Node.js uses a thread pool to handle the IO and one single other to run JS, which is kind of similar as far as I can understand these things. Now, I’ve tried hello world Rails and Sinatra projects, and couldn’t get any concurrency: requests were basically serialized.

So here is what ab -n 1000 -c 10 http://127.0.0.1:2345/ have shown me. I’ve run every one of the tests a few times, and the results jump around but are basically pretty close.

Now, the question I don’t have answered yet is: is this approach used somewhere in Ruby web servers?

var http = require('http'),
request = require('request');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('hello world');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
~/tmp/node ⌘ ab -n 2000 -c 10 http://127.0.0.1:1337/
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 200 requests
Completed 400 requests
Completed 600 requests
Completed 800 requests
Completed 1000 requests
Completed 1200 requests
Completed 1400 requests
Completed 1600 requests
Completed 1800 requests
Completed 2000 requests
Finished 2000 requests
Server Software:
Server Hostname: 127.0.0.1
Server Port: 1337
Document Path: /
Document Length: 11 bytes
Concurrency Level: 10
Time taken for tests: 0.496 seconds
Complete requests: 2000
Failed requests: 0
Write errors: 0
Total transferred: 224000 bytes
HTML transferred: 22000 bytes
Requests per second: 4036.12 [#/sec] (mean)
Time per request: 2.478 [ms] (mean)
Time per request: 0.248 [ms] (mean, across all concurrent requests)
Transfer rate: 441.45 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 1
Processing: 1 2 0.6 2 7
Waiting: 1 2 0.6 2 7
Total: 1 2 0.5 2 7
Percentage of the requests served within a certain time (ms)
50% 2
66% 2
75% 3
80% 3
90% 3
95% 3
98% 4
99% 5
100% 7 (longest request)
~/tmp/node ⌘
require 'socket'
require 'net/http'
server = TCPServer.new('localhost', 2345)
Thread.abort_on_exception = true
def respond(socket)
request = socket.gets
STDERR.puts request
Thread.new do
response = 'hello world'
socket.print "HTTP/1.1 200 OK\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: #{response.bytesize}\r\n" +
"Connection: close\r\n\r\n#{response}"
socket.close
end
end
loop do
respond server.accept
end
~/tmp/ruby ⌘ ab -n 2000 -c 10 http://127.0.0.1:2345/
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 200 requests
Completed 400 requests
Completed 600 requests
Completed 800 requests
Completed 1000 requests
Completed 1200 requests
Completed 1400 requests
Completed 1600 requests
Completed 1800 requests
Completed 2000 requests
Finished 2000 requests
Server Software:
Server Hostname: 127.0.0.1
Server Port: 2345
Document Path: /
Document Length: 11 bytes
Concurrency Level: 10
Time taken for tests: 0.473 seconds
Complete requests: 2000
Failed requests: 0
Write errors: 0
Total transferred: 190000 bytes
HTML transferred: 22000 bytes
Requests per second: 4228.35 [#/sec] (mean)
Time per request: 2.365 [ms] (mean)
Time per request: 0.236 [ms] (mean, across all concurrent requests)
Transfer rate: 392.28 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 2
Processing: 1 2 1.2 2 13
Waiting: 0 2 1.2 2 13
Total: 1 2 1.2 2 13
Percentage of the requests served within a certain time (ms)
50% 2
66% 2
75% 2
80% 2
90% 3
95% 3
98% 5
99% 11
100% 13 (longest request)
~/tmp/ruby ⌘
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment