ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

RustによるRubyアプリケーションサーバー Flamboyant: version 0.1.0.rc

「なんとか動かせた」という世界の話なんですが、途中経過をメモしておきます。

先日Rustでgemをなるべくイマい形で書く方法をメモっていたんですが、一つは実用的なものを作ろうと思ってサーバを書いています。

udzura.hatenablog.jp

Flamboyant という名前をつけました。

github.com

RubyGems 3.4.0.dev (今のgithub masterにあるやつ)を使っていればRuby自体は3系であれば動くようです。インストールは:

gem install flamboyant --pre

あるいはGemfileに:

# 2022/06/11 現在
gem "flamboyant", "0.1.0.rc2"

実装的にはTRPLの サーバ実装ワーク にほぼ近いようなコードで、さらにリクエストとレスポンスを構築するとこは生のstringをRubyに渡してWEBrickのコードをほぼそのまま使ってやっていってる感じなので、これは「書いた」と言っていいのでしょうか...?

しかしそれで Rack::Lint は通過させました。そう、Flamboyantは歴としたRack互換のアプリケーションサーバです。したがって最低限ベンチは流せるようになったと思います。

あとflamboyantの中ではcoffret使ってるんですが、当座はFlamboyantの中に置いてるようなRuby C APIラッパーコードもあって、その辺はなんかいい感じに整理します。


この実装ではこうなってしまうという記録を残しておきます。まずApache Benchで、WEBrick、Puma(いずれもデフォルト設定)との比較:

Rubyのアプリケーションはこういう config.ru を書いてます。

run lambda {|_env|
  return [200,
          {"Content-Type" => "text/plain"},
          ["OK Rack"]]
}

ABのパラメータはとりあえずソフトです。

$ ab -n 10000 -c 1 http://127.0.0.1:9292/

webrick

Concurrency Level:      1
Time taken for tests:   3.997 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1740000 bytes
HTML transferred:       70000 bytes
Requests per second:    2501.79 [#/sec] (mean)
Time per request:       0.400 [ms] (mean)
Time per request:       0.400 [ms] (mean, across all concurrent requests)
Transfer rate:          425.11 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.8      0      64
Processing:     0    0   0.1      0       3
Waiting:        0    0   0.1      0       3
Total:          0    0   0.8      0      65

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      0
  95%      0
  98%      1
  99%      1
 100%     65 (longest request)

Puma

Concurrency Level:      1
Time taken for tests:   2.932 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      710000 bytes
HTML transferred:       70000 bytes
Requests per second:    3410.32 [#/sec] (mean)
Time per request:       0.293 [ms] (mean)
Time per request:       0.293 [ms] (mean, across all concurrent requests)
Transfer rate:          236.46 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    0   0.1      0       4
Waiting:        0    0   0.1      0       4
Total:          0    0   0.1      0       5

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      0
  95%      0
  98%      0
  99%      0
 100%      5 (longest request)

Flamboyant

Concurrency Level:      1
Time taken for tests:   2.516 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1100000 bytes
HTML transferred:       70000 bytes
Requests per second:    3974.32 [#/sec] (mean)
Time per request:       0.252 [ms] (mean)
Time per request:       0.252 [ms] (mean, across all concurrent requests)
Transfer rate:          426.93 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   2.5      0     249
Processing:     0    0   0.0      0       1
Waiting:        0    0   0.0      0       1
Total:          0    0   2.5      0     250

Percentage of the requests served within a certain time (ms)
  50%      0
  66%      0
  75%      0
  80%      0
  90%      0
  95%      0
  98%      0
  99%      0
 100%    250 (longest request)

(よく考えたらヘッダが違うので、例えば「Total transferred」がかなり違うんですよね。あくまで参考レベルということで...)

で、Flamboyantはこのベンチは完走します(偉い!自画自賛する)。そして、単純にRPSだけ見るとなぜかPumaより少し優秀です。これは... サーバとして必要な機能が全然実装されていないこと、例えばそもそもログを出してないなどに起因してると思います...。ログってやっぱり影響あるんですかね...。

で、そもそもFlamboyantは他のサーバに比べ、レスポンスタイムにかなりばらつきがあることが示唆されていそうです。

次はwrkを使ってみます。こういうパラメータで。

$ wrk -t 1 -c 1 -d 5s --latency http://127.0.0.1:9292/

WEBrick

Running 15s test @ http://localhost:9292/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   320.14us  127.40us   8.37ms   98.34%
    Req/Sec     3.16k   137.43     3.55k    85.43%
  Latency Distribution
     50%  305.00us
     75%  338.00us
     90%  362.00us
     99%  473.00us
  47478 requests in 15.10s, 8.11MB read
Requests/sec:   3144.27
Transfer/sec:    549.64KB

Puma

Running 15s test @ http://localhost:9292/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   190.04us   45.43us   1.89ms   93.72%
    Req/Sec     5.28k   176.92     5.48k    96.03%
  Latency Distribution
     50%  176.00us
     75%  193.00us
     90%  228.00us
     99%  342.00us
  79377 requests in 15.10s, 5.37MB read
Requests/sec:   5256.73
Transfer/sec:    364.48KB

Flamboyant

Running 15s test @ http://localhost:9292/
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   427.75us    3.87ms  87.81ms   99.45%
    Req/Sec     4.46k     1.05k    4.98k    88.89%
  Latency Distribution
     50%  156.00us
     75%  192.00us
     90%  211.00us
     99%  317.00us
  16337 requests in 15.05s, 1.71MB read
  Socket errors: connect 0, read 16337, write 0, timeout 0
Requests/sec:   1085.75
Transfer/sec:    116.63KB

Flamboyantは明らかに完走していなくて、並行数1ですら、ちょっと負荷を上げると今今は刺さるということがわかります。〜完〜*1

むしろWEBRickって安定してて偉いんですね。

とはいえ、条件によってはちゃんと動くということを自画自賛して行きたくて、今後はまともな機能を実装しつつ、刺さる箇所を調査したり、どれくらい遅くなったり速くなったりしたか観察しながら手を動かすというのをやっていこうと思います。

というメモを残した。


比較した環境です:

GEM
  remote: https://rubygems.org/
  specs:
    flamboyant (0.1.0.rc2)
      rack
      webrick
    nio4r (2.5.8)
    puma (5.6.4)
      nio4r (~> 2.0)
    rack (2.2.3.1)
    webrick (1.7.0)

PLATFORMS
  arm64-darwin-21

DEPENDENCIES
  flamboyant (= 0.1.0.rc2)
  puma
  webrick

BUNDLED WITH
   2.4.0.dev
$ ruby -v
ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [arm64-darwin21]

マシンはApple M1 Max(8+2 Core, メモリ64GB)です。

*1:しかし、それでも通ったリクエストはPumaより高速な傾向がある、か?