Recently i’ve been playing around with xen and different hosting solutions, and i was wondering about lightweight, yet performant replacements for the usual apache + mod_fcgi + dispatcher stack. I did toy around with nginx before, together with mongrel with quite good success.
But it seems there is some serious competition for mongrel coming along, the Thin webserver. It combines the good points of mongrel, the HTTP parser, together with an event driven IO framework called eventmachine. The Bechmarks of it look promising:
Its clearly to see the event driven IO approach is clearly superiour to the others when having many concurent requests.
Now why nginx when thin already performs so good ? Well, for one thing, thin is still in its quite early stages of production. Also, when it comes to serving static files the ruby version does not even come close. Another thing that comes to play is that nowerdays most CPU’s are multi core, while ruby is single threated. That means the concurrent requests will be served by one CPU.
Here nginx comes to play, its an excellent http server, proxy and load balancer. One could start serveral rails servers with mongrel (mongrel_cluster is an excellent tool for that), or many thin servers. Right now i’m using the following rake task (from Stepehn Celis) for starting thin:
namespace :thin do namespace :cluster do desc 'Start thin cluster' task :start => :environment do `cd #{RAILS_ROOT}` port_range = RAILS_ENV == 'development' ? 3 : 8 (ENV['SIZE'] ? ENV['SIZE'].to_i : 4).times do |i| Thread.new do port = ENV['PORT'] ? ENV['PORT'].to_i + i : ("#{port_range}%03d" % i) str = "thin start -d -p#{port} -Ptmp/pids/thin-#{port}.pid" str += " -e#{RAILS_ENV}" puts str puts "Starting server on port #{port}..." `#{str}` end end end desc 'Stop all thin clusters' task :stop => :environment do `cd #{RAILS_ROOT}` Dir.new("#{RAILS_ROOT}/tmp/pids").each do |file| Thread.new do if file.starts_with?("thin-") str = "thin stop -Ptmp/pids/#{file}" puts "Stopping server on port #{file[/\d+/]}..." `#{str}` end end end end end end
with that you can start/stop many thin instances easily:
# rake thin:cluster:start RAILS_ENV=production SIZE=3 PORT=8000 # rake thin:cluster:stop
Getting those instances into nginx is also easy, the following example layout works with the ubuntu nginx package:
upstream thin { server 127.0.0.1:8000; server 127.0.0.1:8001; server 127.0.0.1:8002; } server { listen 80; server_name localhost; access_log /var/log/nginx/localhost.access.log; root /var/www/test/public; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect false; if (-f $request_filename/index.html) { rewrite (.*) $1/index.html break; } if (-f $request_filename.html) { rewrite (.*) $1.html break; } if (!-f $request_filename) { proxy_pass http://thin; break; } } }
To see if it is really working i used apache Bench on the the same setup with a simple dynamic page on a dual cpu p3-s 1,3 Ghz machine :
# ab -n 1000 -c 10 http://10.1.4.99/foo/ This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright 2006 The Apache Software Foundation, http://www.apache.org/ Benchmarking 10.1.4.99 (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Finished 1000 requests Server Software: nginx/0.5.26 Server Hostname: 10.1.4.99 Server Port: 80 Document Path: /foo/ Document Length: 59 bytes Concurrency Level: 10 Time taken for tests: 6.247127 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 505000 bytes HTML transferred: 59000 bytes Requests per second: 160.07 [#/sec] (mean) Time per request: 62.471 [ms] (mean) Time per request: 6.247 [ms] (mean, across all concurrent requests) Transfer rate: 78.92 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.7 0 9 Processing: 7 61 72.9 35 453 Waiting: 0 61 72.9 34 453 Total: 7 61 72.9 35 453 Percentage of the requests served within a certain time (ms) 50% 35 66% 59 75% 82 80% 97 90% 134 95% 203 98% 333 99% 380 100% 453 (longest request)
The same on one of the internal thin servers would give the following result:
# ab -n 1000 -c 10 http://10.1.4.99:8000/foo/ This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0 Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright 2006 The Apache Software Foundation, http://www.apache.org/ Benchmarking 10.1.4.99 (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests Finished 1000 requests Server Software: Server Hostname: 10.1.4.99 Server Port: 8000 Document Path: /foo/ Document Length: 59 bytes Concurrency Level: 10 Time taken for tests: 7.880520 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 446000 bytes HTML transferred: 59000 bytes Requests per second: 126.90 [#/sec] (mean) Time per request: 78.805 [ms] (mean) Time per request: 7.881 [ms] (mean, across all concurrent requests) Transfer rate: 55.20 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 1 Processing: 63 78 50.9 63 252 Waiting: 61 77 50.8 62 251 Total: 63 78 50.9 63 252 Percentage of the requests served within a certain time (ms) 50% 63 66% 63 75% 64 80% 64 90% 65 95% 251 98% 251 99% 252 100% 252 (longest request)
As you can see, the nginx version scales quite a bit better with 10 concurrent users.
Now what is really missing is some nice scripted integration into startup scripts, so you can automaticly start/stop many nginx/thin installations at bootup (and not execute some manual rake tasks :))
Pingback: links for 2008-01-16 « Bloggitation
Pingback: Thin 0.5.3 “Purple Yogurt” — Learning on Rails
Pingback: DriftSand » Thin webserver
Pingback: Recent Links Tagged With "rubyonrails" - JabberTags
Pingback: Gentoo下Nginx+thin构建rails环境 - Elton's Blog
Pingback: Gentoo下Nginx+thin构建rails环境 | ByPat博客专注于Linux资源分享!