Handler vs. Rack on Passenger

先日、アクセスを記録するだけの簡単なWebアプリを作ろうとして同僚のTさんに相談したところ「素のRackアプリがおすすめですよ〜」とのコメントをいただいた。
なんとなくmod_rubyApacheのHandlerとして実装してしまうのがいいかな…と考えていたのだが、趣味を通すと楽しくないことになりそうなので、Rackアプリで実装する方向に心は傾きつつある。

ま、それはそれとして、実際、Handlerとして実装するのとPassengerを使うのでは、どのくらいパフォーマンスに差があるのか知りたかったので、簡単に計測してみた。

計測方法

いつものようにEC2で計測。アクセス毎にsyslogに書きにいくだけの簡単なプログラムを、Handler(C, Ruby)とRackで実装して、Siege×5で叩いてみた。

Handler(C版)

#include "httpd.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"

#include <sys/syslog.h>
#include <time.h>

static int syslog_handler(request_rec *r) {
  time_t curr;
  struct tm *local;

  if (strcmp(r->handler, "syslog")) {
    return DECLINED;
  }

  time(&curr);
  local = localtime(&curr);

  syslog(LOG_INFO, "%04d/%02d/%02d %02d:%02d:%02d",
         local->tm_year + 1900, local->tm_mon + 1, local->tm_mday,
         local->tm_hour, local->tm_min, local->tm_sec);

  r->content_type = "text/plain";

  if (!r->header_only) {
    ap_rputs("OK", r);
  }

  return OK;
}

static void syslog_register_hooks(apr_pool_t *p) {
  ap_hook_handler(syslog_handler, NULL, NULL, APR_HOOK_MIDDLE);
}

module AP_MODULE_DECLARE_DATA syslog_module = {
  STANDARD20_MODULE_STUFF,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  syslog_register_hooks
};
<Location /csyslog>
  SetHandler syslog
</Location>

Handler(Ruby版)

require 'singleton'
require 'syslog'

module Apache
  class ModSyslog
    include Singleton

    def handler(r)
      Syslog.open
      Syslog.info(Time.now.strftime '%Y/%m/%d %H:%M:%S')
      Syslog.close
      r.print('OK')
      return OK
    end
  end
end
LoadModule ruby_module modules/mod_ruby.so

RubyRequire apache/syslog
RubySafeLevel 0

<Location /syslog>
  SetHandler  ruby-object
  RubyHandler Apache::ModSyslog.instance
</Location>

Rackアプリ

require 'syslog-app'

map '/' do
  run SyslogApp.new
end
require 'rubygems'
require 'rack'

require 'syslog'

class SyslogApp
  def call(env)
    Syslog.open
    Syslog.info(Time.now.strftime '%Y/%m/%d %H:%M:%S')
    Syslog.close

    [200, {"Content-Type" => "text/plain"}, ["OK"]]
  end
end
LoadModule passenger_module /usr/lib64/ruby/gems/1.8/gems/passenger-2.2.15/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib64/ruby/gems/1.8/gems/passenger-2.2.15
PassengerRuby /usr/bin/ruby

PassengerHighPerformance on
PassengerMaxPoolSize 256

Listen 8080

<VirtualHost *:8080>
  DocumentRoot /var/www/syslog/public
</VirtualHost>

結果

trans/sec
Hander(C版) 4817.4
Hander(Ruby版) 3006.15
Rack 1160.19

所感

  • RackアプリにくらべるとさすがにHandlerのスループットの方が高いが、3倍の差を大きいと考えるかどうかは状況によって異なりそう
  • syslogじゃなくてDBに書き込むようにすると同じぐらいのスループットになるんじゃなかろうか?
  • MaxClientsを増やしたら、Handlerのスループットはもう少しあがるかも
  • PassengerMaxPoolSizeを256にしているが、実際のRackのプロセス数は30ぐらいだった
  • Rackアプリはあまり詳しくないので何か設定ミスをしているかもしれない

おまけ

httpdとRackのメモリ使用状況。
Rackはなんにもしなくても10MBか…


--------- Apache processes ---------
PID PPID VMSize Private Name
------------------------------------
4050 1 190.2 MB 0.3 MB /usr/sbin/httpd
5208 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5209 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5211 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5212 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5213 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5214 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5215 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5216 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5217 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5218 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5219 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5220 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5221 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5222 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5223 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5224 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5225 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5226 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5227 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5229 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5230 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5233 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5234 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5235 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5236 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5237 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5238 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5239 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5240 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5241 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5242 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5244 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5245 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5246 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5248 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5249 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5250 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5252 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5253 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5254 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5255 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5256 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5257 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5258 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5259 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5261 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5262 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5263 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5264 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5266 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5268 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5269 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5270 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5271 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5273 4050 193.2 MB 1.3 MB /usr/sbin/httpd
5274 4050 193.2 MB 1.3 MB /usr/sbin/httpd
### Processes: 57
### Total private dirty RSS: 73.99 MB


-------- Nginx processes --------

### Processes: 0
### Total private dirty RSS: 0.00 MB


---- Passenger processes -----
PID VMSize Private Name
------------------------------
4057 145.4 MB 1.4 MB /usr/lib64/ruby/gems/1.8/gems/passenger-2.2.15/ext/apache2/ApplicationPoolServerExecutable 0 /usr/lib64/ruby/gems/1.8/gems/passenger-2.2.15/bin/passenger-spawn-server /usr/bin/ruby /tmp/passenger.4050
4058 48.9 MB 8.2 MB Passenger spawn server
4283 54.8 MB 10.3 MB Rack: /var/www/syslog
4289 55.5 MB 11.0 MB Rack: /var/www/syslog
4291 55.5 MB 11.0 MB Rack: /var/www/syslog
4295 55.0 MB 10.6 MB Rack: /var/www/syslog
4297 55.5 MB 11.0 MB Rack: /var/www/syslog
4303 55.5 MB 10.9 MB Rack: /var/www/syslog
4305 55.5 MB 11.0 MB Rack: /var/www/syslog
4309 55.5 MB 11.0 MB Rack: /var/www/syslog
4317 55.5 MB 11.0 MB Rack: /var/www/syslog
4322 55.5 MB 11.0 MB Rack: /var/www/syslog
4325 55.5 MB 11.0 MB Rack: /var/www/syslog
4327 55.5 MB 10.9 MB Rack: /var/www/syslog
4345 55.5 MB 11.0 MB Rack: /var/www/syslog
4347 55.0 MB 10.5 MB Rack: /var/www/syslog
4349 55.5 MB 11.0 MB Rack: /var/www/syslog
4353 54.9 MB 10.4 MB Rack: /var/www/syslog
4361 54.8 MB 10.3 MB Rack: /var/www/syslog
4491 54.8 MB 10.3 MB Rack: /var/www/syslog
4685 54.8 MB 10.3 MB Rack: /var/www/syslog
4705 55.5 MB 10.9 MB Rack: /var/www/syslog
4709 54.8 MB 10.3 MB Rack: /var/www/syslog
4717 55.5 MB 11.0 MB Rack: /var/www/syslog
4719 55.5 MB 11.0 MB Rack: /var/www/syslog
4721 55.5 MB 11.0 MB Rack: /var/www/syslog
5014 55.5 MB 10.9 MB Rack: /var/www/syslog
5020 55.5 MB 11.0 MB Rack: /var/www/syslog
5030 55.5 MB 11.0 MB Rack: /var/www/syslog
5032 54.8 MB 10.3 MB Rack: /var/www/syslog
5034 54.8 MB 10.3 MB Rack: /var/www/syslog
5036 55.6 MB 11.0 MB Rack: /var/www/syslog
5038 55.5 MB 10.9 MB Rack: /var/www/syslog
### Processes: 33
### Total private dirty RSS: 343.31 MB