いくつかのWebフレームワークのベンチマークを雑にとってみた

この記事は、[富士通クラウドテクノロジーズ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/fjct) の18日目の記事です。

17日目は **@abej さんの [海外ITカンファレンス動画のすすめ](https://note.mu/abej/n/n83b0175f4d5e)** でした！

## 🌞はじめに

NIFCLOUD の API を開発している @kzmake です。最近、趣味で色々なWebフレームワークを触ることにはまってたり。

今日では様々なWebフレームワークが存在しており、それを利用して手軽にWebアプリケーションを開発できるようになっていますよね。ただ、あまりにも種類が多く、Webフレームワークの選定基準に何を選択するか悩む場面もあるのではないでしょうか。

本記事では、負荷検証をのWebフレームワークに対して実施し、それぞれの **「処理能力」**に着目して簡単な比較を行ってみたいと思っています。

※下記に影響されて検証してみます

* [TechEmpower/FrameworkBenchmarks](https://github.com/TechEmpower/FrameworkBenchmarks)
* [costajob/app-servers](https://github.com/costajob/app-servers)

## 📝注意書き

* あくまで検証のため綺麗な記述や言語毎に十分な最適化をおこなっているわけではありません。フレームワーク毎適切な記述をすることで改善される場合も十分あります。
* 言語の宣伝や批判を行うものではありません。 言語やアプリケーションサーバー、フレームワーク毎にそれぞれ優れている点があります。


## 💡TL;DR

* jester(Nim) や japronto(python) がとってもはやい
* 言語的に　golang / Scala / Rust / Nim あたりが全体的にはやい
* flask(python) / Ruby on Rails(Ruby) は他と比べると低RPS


## 🤔Webアプリケーションの「速さ」とは何をもとに考えるべきか？

Webアプリケーションの「速さ」といえばなんでしょうか？

* 応答時間の速さ
* RPS(Requests per second)
* Time To First Byte (TTFB)

なんかがありますよね。これについては、こちらの記事がとてもわかりやすくまとめてくださっているので、本記事では割愛させてもらいます。

[負荷試験のためのノウハウと Webフレームワークの負荷試験 (Python,Node,Go,PHP)](https://qiita.com/tkngue/items/62101788c0f384a5b12e)


今回は、単純に**Webフレームワークの処理能力** を測るので **リクエストのスループットとして RPS(Requests per second)**に着眼してそれぞれのWebフレームワークを見てみたいと思います。

## ⚙️検証内容

### 作成するWebアプリケーション

下記の機能を持つ必要最低限のWebアプリケーションをそれぞれのWebフレームワークを利用して作成する。

#### 機能

* `text/plain` としてレスポンスを返却
* `Hello, World!` の文字列を返却する

### 検証方法

今回は、**[wg/wrk](https://github.com/wg/wrk)** を利用して検証してみたいと思います。
(今回は RPS を測定するのみなので Coordinated Omission補正込みの [giltene/wrk2](https://github.com/giltene/wrk2) は使用しません)

本記事では、 **5回計測し、 RPS がもっとも良い結果をピックアップ**するものとします。

### 環境

今回の検証では [NIFCLOUD](https://cloud.nifty.com/) のクラウドサービスを利用して検証しました。

#### Webアプリケーションサーバー(負荷試験対象)

* NIFCLOUD type: large
* Intel(R) Xeon(R) CPU E5-2697 v3 @ 2.60GHz (4 cores)
* 4 GB
* Ubuntu 18.04 LTS

#### 負荷試験用サーバー(wrk実行用)

* NIFCLOUD type: large
* Intel(R) Xeon(R) CPU E5-2697 v3 @ 2.60GHz (4 cores)
* 16 GB
* Ubuntu 18.04 LTS

#### Network

PrivateLan を作成し、Webアプリケーションサーバー(AppServer) と 負荷試験用サーバー(Load) 間に Network を構築します。

[AppServer] : 192.168.10.10 ----------- [Load] : 192.168.10.5

#### Settings

このあたりは適当に。

```
/etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_dynaddr = 1
net.ipv4.tcp_rfc1337 = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_keepalive_probes = 5
net.ipv4.tcp_slow_start_after_idle = 0
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
```
```
/etc/security/limits.conf
*    soft nofile 65536
*    hard nofile 65536
root soft nofile 65536
root hard nofile 65536
```
```
/etc/pam.d/common-session
session    required     pam_limits.so
```

## 検証用アプリケーション

* [kzmake/benchmark-app-servers](https://github.com/kzmake/benchmark-app-servers) 

## 🔥検証結果

### Results

同時接続数`100`のときのRPSでsort。

c: connections

| Language  |         Frameworks          | RPS(c=10)  | RPS(c=100)  |  RPS(c=1K)  |
| :-------- | :-------------------------- | ---------: | ----------: | ----------: |
| Python    | japronto                    | `35192.51` | `182209.20` | `313111.38` |
| Rust      | hyper                       | `31478.74` | `181978.18` | `250753.83` |
| Nim       | jester                      | `37875.16` | `180562.05` | `267158.10` |
| Golang    | fasthttp                    | `32896.92` | `178742.38` | `272827.53` |
| Scala     | colossus                    | `30223.88` | `153630.81` | `173208.76` |
| Python    | gunicorn + meinheld         | `30511.41` | `142269.85` | `147096.10` |
| Golang    | net/http                    | `27543.16` | `112160.14` | `126247.31` |
| Golang    | echo                        | `27501.64` | `107365.95` | `121058.50` |
| Golang    | gin                         | `27195.70` | `102053.41` | `117794.30` |
| Scala     | Finatra                     | `25884.09` | `101039.92` |  `95836.10` |
| Rust      | iron                        | `32352.37` |  `79042.21` |  `78359.21` |
| Scala     | finch                       | `23548.16` |  `67201.73` |  `67690.82` |
| Elixir    | cowboy                      | `21982.09` |  `62189.22` |  `58050.46` |
| Scala     | akka-http                   | `14829.31` |  `60694.92` |  `65393.73` |
| Scala     | Play Framework 2.x + netty  | `21157.73` |  `54774.22` |  `59055.88` |
| Elixir    | cowboy + plug               | `20930.40` |  `48501.17` |  `42336.23` |
| Scala     | http4s                      | `20936.95` |  `41011.60` |  `41406.29` |
| Elixir    | cowboy2 + plug              | `17279.92` |  `33547.23` |  `30558.31` |
| Ballerina | Ballerina                   | `13055.05` |  `26570.28` |  `32383.85` |
| Python    | gunicorn + flask + meinheld | `15485.77` |  `21216.30` |  `20185.63` |
| Elixir    | cowboy + phoenix            | `15972.04` |  `19897.84` |  `29068.48` |
| Python    | bjoern + bottle             | `14890.77` |  `17342.36` |  `16298.68` |
| Elixir    | cowboy2 + maru              | `13616.37` |  `15345.62` |  `14410.75` |
| Python    | gunicorn                    |  `7679.21` |  `11505.21` |  `10999.50` |
| Node.js   | express                     |  `9871.12` |   `9844.02` |   `9687.88` |
| Python    | uwsgi + flask               |  `6575.53` |   `8327.47` |   `8272.04` |
| Python    | gunicorn + flask            |  `3961.87` |   `7499.01` |   `7328.44` |
| Python    | gunicorn + django           |  `3459.65` |   `4085.68` |   `5958.89` |
| Ruby      | Ruby on Rails 5.x + unicorn |  `3990.49` |   `3748.68` |   `3941.27` |
| Python    | bottle                      |  `2403.29` |   `2318.08` |   `1752.36` |
| Ruby      | Ruby on Rails 5.x + puma    |  `3075.53` |   `2134.67` |   `2241.91` |
| Python    | flask                       |  `1077.82` |   `1207.28` |   `1205.87` |

### メモ：細かいデータ

* [kzmake/benchmark-app-servers](https://github.com/kzmake/benchmark-app-servers) 
    * 各言語のVersion情報やwrkの結果

## 🌜おわりに

この記事では、スループットのみに着眼し、できるだけ広い言語で様々なWebフレームワークをテストしてみました。

結果としては、単純にスループットだとGolang、Scala、NimやRustが安定して高RPS、PythonやRubyは他と比べて低RPSとなった。ただ、pythonでもjaprontoが最も高いRPSを記録するといった面白い結果がみられました。

他にも、全体的に低RPSのpythonでも`gunicorn + flask + meinheld`の組み合わせなどで高いRPSもだせることもわかりました。

今回は単純にPlaintextを返すだけのアプリケーションでしたが、より複雑なベンチマークを行っている

[TechEmpower / Web Framework Benchmarks](https://www.techempower.com/benchmarks/)

も見てみるとおもしろいかもしれません！

みなさんの普段使っているWebフレームワークもテストし、比較してみてはいかがでしょうか？
長い記事にお付き合いいただき、ありがとうございました！


明日は **@YOMOGItaro さんが 「NIFCLOUD で Elastic Cloud Enterprise」** について書いてくれるみたいです。お楽しみに！