Thinは遅い?

「Heroku でアプリケーションサーバを Uniron (or Puma, etc) にしたらn倍速くなったぜ!」みたいな話をたまに見掛けますが、本当なんでしょうか。実験してみましょう。

テスト環境

アプリケーションは Rack で、50msec の sleep の後に 500KB のレスポンスを返します。各サーバに対して100回のリクエストを、同時接続数を 1-20 の間で変えつつ投げました。詳しくはソースを見てください。

Chart 1

(凡例の c は concurrency、同時接続数です)

はい、どう見ても Thin は遅いです。まったくスケールしません。本当にありがとうございました。

ここでおもむろにパケットを遅延させてみましょう。Linux だと Network Emulator (netem) なるものを使えばいいみたいです。

# tc qdisc add dev lo root netem delay 50ms
# ping -c4 localhost
PING trash.ursm.jp (127.0.0.1) 56(84) bytes of data.
64 bytes from trash.ursm.jp (127.0.0.1): icmp_seq=1 ttl=64 time=100 ms
64 bytes from trash.ursm.jp (127.0.0.1): icmp_seq=2 ttl=64 time=100 ms
64 bytes from trash.ursm.jp (127.0.0.1): icmp_seq=3 ttl=64 time=100 ms
64 bytes from trash.ursm.jp (127.0.0.1): icmp_seq=4 ttl=64 time=100 ms

--- trash.ursm.jp ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 100.044/100.048/100.056/0.387 ms

指定したインターフェイス (ここでは lo) の出力に作用します。行き帰りとも lo を経由しているので合計 100ms になるわけですね。太平洋を往復するとだいたいこんなものでしょうか。

この状態でさっきと同じベンチマークを走らせてみます。

Chart 2

なんということでしょう。他のサーバがワーカ数で頭打ちになっているのを尻目に、Thin だけがスループットを上げています。さらに --threaded オプションを付けるとその差は圧倒的になります (ただし experimental です)。

Chart 3

Unicorn 他ではリクエストを受けてレスポンスを返し終わるまでワーカが占有されてしまうのに対し、Thin はレスポンスさえ生成してしまえば次のリクエストを捌くことができる、という違いによりこのような差が出ます。

なお、この状況で Unicorn 他が遅くなるのは別にこれらのサーバが悪いわけではなくて、デザインによるものです。Unicorn のドキュメント から引用すると:

Instead of attempting to be efficient at serving slow clients, unicorn relies on a buffering reverse proxy to efficiently deal with slow clients.

「スロークライアントへの対処はあえて省いてあるから、ちゃんとバッファリングするリバースプロキシ (nginx とか) と組み合わせて使ってね」ということです。

(Rainbows! はスロークライアントを考慮してあるはずなんですが、なぜか Unicorn と似たり寄ったりでした)

まとめ

アプリケーションサーバが直接露出する環境 (e.g. Heroku) では、状況により Thin が一番高速になる場合があります。Unicorn などのモダンなサーバはリバースプロキシと組み合わせて使うようにしましょう。