業務でAPIサーバーを作る事になり、何で実装しようか検討したという話です。
候補としてRails(定番)が上がっていたものの、
- 大量のリクエストを捌く必要があるので大変そう
- ビジネスの成長に合わせてスケールさせ辛い(スゴい勢いで成長する)
- そこまで複雑な処理はしない(別のサブシスでごりごりやる、こっちはRails製)
という事で他の選択を検討しました。
選択しとして
- Erlang ( + cowboy )
- Elixir ( + cowboy )
- Scala ( + akka )
- Go
があがったので、RailsとElixir/cowboyでAPIサーバーのプロトタイプを作ってベンチマーク比較をしてみました。
※ ElixirはErlangと同水準のBeamを吐くのでErlangは除外しています
(実際に計測した結果、同程度の性能でした、こちらは別途結果をまとめて公開します)
※ Go/Scalaについては僕自身そこまで熟知しているわけではないので今回は対象から外しています
以下、プロトタイプAPIサーバーの仕様です
- pingリクエストでpongを返す(http://bench-server/ping -> pong)
- mysqlからデータをJSONで取得、ただし複雑なクエリは投げない(http://bench-server/users -> {user's json list})
ソースコードはこちら
使用したサーバー/ライブラリは
- AWS/EC2(t2microインスタンス)
- 64bit CentOS7
- Rails: Rails4.2 + Ruby2.2 + nginx1.6.2 + unicorn4.8.2
- Elixir: Erlang17.4 + Elixir1.0.2 + Cowboy2.0
- somaxconnsの値: 65535
- nofileの値: 65535
以上の環境でabを使い、以下の様に計測しました。
- DBアクセスをしない(ボトルネックにならない)、ping-APIに対してリクエストをかける
- 各同時接続数毎に5回測定を行いその平均を結果とする( ab -n <同時接続数> -c <同時接続数> target-url )
- 同時接続を投げてエラーにならない最大同時接続数
- ElixirとRailsの最大接同時続数の Request/Sec、Time/Req を測定
2/14追記1
コメントのご指摘通り、somaxconnの(デフォルト128の)値でbacklogsが上書きされていた為、railsのベンチマークの最大上限が130(128)となっていました。
somaxconnの値を65535に変更してベンチを取り直しました。
2/14追記2
cowboy(Ranch)のmax_connectionsがファイルディスクリプタ数に依存する為、nofileの値を65535に変更してあります。
結果は以下となります。
Rails | Elixir | |
---|---|---|
(Failedが出ない)最大同時接続数 |
|
4900 |
最大同時接続時のRequest/Sec |
|
4175 |
最大同時接続時のTime/Req(1リクエストあたり) |
|
0.37ms |
Railsは(もちろんElixir/cowboyも)チューニングの余地がかなりあり、まだまだベンチ結果を上げる事は可能と思います。
ただどう頑張っても(僕のスキルでは)Elixir/cowboyでの結果並みにRailsをチューニングで性能改善ができるイメージはわきませんでした。
(またRailsではなくSinatra等の軽量F/Wを使えばもっと良いベンチマーク結果がでると思います)
また、このベンチマークではDBアクセスがありません(単純なping)が、DBアクセスのあるAPIのベンチマークを取れば、Elixir/cowboyはDBがボトルネックになりここまでの性能は出ないと思います。(事実DBがボトルネックになり性能劣化しました)
以上をふまえて、僕の携わってるプロジェクトにてElixirの採用を決定しました。
(注意)
このエントリは「Rails遅いよ」という事を主張するものではありません。
事実、ビジネスロジックが多く実装される管理システムはRailsを採用しております。
要は適材適所という話で、たまたまElixir/Cowboyという選択肢があったので採用したのです。
はい。