GazelleでやったことをRubyでもやってみようと思い、まず picohttpparser の Ruby バインディングと、perforkなサーバを書く時に便利なモジュールであるParallel::PreforkのRuby版を書いてリリースしました。
pico_http_parser
http://rubygems.org/gems/pico_http_parser
prefork_engine
http://rubygems.org/gems/prefork_engine
そしてこの2つを使って、StarletのRuby版を書いてみました。ソースコードはprefork_engineのrepositoryにあります。
https://github.com/kazeburo/prefork_engine/blob/master/example/starlet.rb
動かし方
まず上の2つのgemをいれます。bundlerを使っても良いですね
$ gem install pico_http_parser prefork_engine
prefork_engineのrepositoryをcloneしてきてexampleディレクトリに移動
$ git clone https://github.com/kazeburo/prefork_engine.git
$ cd prefork_engine/example
hello worldなサンプルアプリケーションも同梱されているので、そいつを動かしてみます
$ rackup -r ./starlet.rb -s Starlet -O MaxWorkers=5 -O MaxRequestPerChild=1000 config.ru
curlでアクセスすると
$ curl -sv http://localhost:9292/|less
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9292 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.38.0
> Host: localhost:9292
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Connection: close
< Content-Type: text/html
< Date: Thu, 11 Dec 2014 07:00:30 GMT
< Server: RubyStarlet
<
{ [data not shown]
* Closing connection 0
hello world
ちゃんと出て来た。大丈夫そう!
軽くベンチマーク
環境はEC2のc3.4xlarge、Amazon Linuxを使いました。nginxのうしろにアプリケーションサーバとして動作させた場合を想定してます。
ベンチマーク準備
Rubyはxbuildを使っていれます。unix domain socketで通信する為にstart_serverというPerl製のコマンドが必要になるのでPerlもいれます。
$ git clone https://github.com/tagomoris/xbuild.git $ ./xbuild/ruby-install 2.1.5 ~/local/ruby-2.1 $ xbuild/perl-install 5.20.1 ~/local/perl-5.20 -j4 $ export PATH=/home/ec2-user/local/ruby-2.1/bin:$PATH $ export PATH=/home/ec2-user/local/perl-5.20/bin:$PATH
nginxはyumでインストールし、次のように設定をしました
worker_processes 4; events { worker_connections 10000; } http { include mime.types; access_log off; sendfile on; tcp_nopush on; tcp_nodelay on; etag off; upstream app { server unix:/dev/shm/app.sock; } server { location / { proxy_pass http://app; } } }
比較のためにunicornもいれました。
$ gem install pico_http_parser prefork_engine unicorn
ruby版Starletの起動コマンド
$ start_server --path /dev/shm/app.sock -- rackup -r ./starlet.rb -E production -s Starlet -O MaxWorkers=12 -O MaxRequestPerChild=500000 config.ru
unicornのconfigと起動コマンド
$ cat unicorn.rb worker_processes 8 preload_app true listen "/dev/shm/app.sock" $ unicorn -E production -c unicorn.rb config.ru
ベンチマーク結果
ruby版Starlet
$ ./wrk -t 2 -c 32 -d 30 http://localhost/ Running 30s test @ http://localhost/ 2 threads and 32 connections Thread Stats Avg Stdev Max +/- Stdev Latency 401.75us 114.12us 5.67ms 80.92% Req/Sec 40.83k 2.90k 53.56k 72.18% 2291892 requests in 30.00s, 384.58MB read Requests/sec: 76397.24 Transfer/sec: 12.82MB
$ ./wrk -t 2 -c 32 -d 30 http://localhost/ Running 30s test @ http://localhost/ 2 threads and 32 connections Thread Stats Avg Stdev Max +/- Stdev Latency 393.75us 188.08us 4.41ms 78.60% Req/Sec 42.66k 6.64k 62.33k 71.41% 2389390 requests in 30.00s, 437.40MB read Requests/sec: 79647.31 Transfer/sec: 14.58MB
picohttpparserがすごく速いというのはありますが、それ以外の部分がRubyだけで書かれているにしては良いベンチマーク結果になっている気がします