エンジニアのタナカです。
UUUMではiOSアプリを開発をしています。
アプリを開発するにあたって、バックエンドに何を使えばいいか悩んだりしませんか? バックエンドを決めるには、様々な側面から考える必要があると思いますが、今回はパフォーマンスだけに着目して検討してみました。
ということでGoとNode.jsで簡易WEBサーバーを作って比較してみたいと思います。おまけでRubyも比較してみました。
パフォーマンスで考えた場合にRubyを選ぶことはあまりないとは思いますが、参考までに。
検証環境
- Go go1.6.2 darwin/amd64
- Node.js v5.10.1
- Ruby ruby 2.2.4p230
※GOMAXPROCSは設定していません(マルチコアを使用できるGoが有利な環境)
検証コード
UNIX TIMEを返すだけの簡単なサーバーを用意しました。
Go
標準パッケージのみで作成
package main import ( "fmt" "net/http" "time" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%d", time.Now().Unix()) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
Node.js
Expressは使わず標準パッケージで
var http = require('http'); var server = http.createServer(function (request, response) { response.writeHead(200, {"Content-Type": "text/plain"}); response.end(String(Math.floor( (new Date()).getTime() / 1000 ))); }); server.listen(8080);
Ruby
こちらもwebrick等は使わずに
require 'socket' require 'time' server = TCPServer.new 8080 loop do client = server.accept client.gets client.puts "HTTP/1.0 200 OK" client.puts "Content-Type: text/plain" client.puts client.puts Time.now.to_i client.close end
比較1
Apache付属のabコマンドを使用しました。 今回は、同時100接続のベンチマークを取りたいと思います。そのため以下のオプションを指定しました。
ab -n 100 -c 100 http://127.0.0.1:8080/
結果1
ローカルで動かしたので正確な値ではありませんが、以下の結果になりました。
Go
Concurrency Level: 100 Time taken for tests: 0.008 seconds Complete requests: 100 Failed requests: 0 Total transferred: 12700 bytes HTML transferred: 1000 bytes Requests per second: 12584.95 [#/sec] (mean) Time per request: 7.946 [ms] (mean) Time per request: 0.079 [ms] (mean, across all concurrent requests) Transfer rate: 1560.83 [Kbytes/sec] received
Node.js
Concurrency Level: 100 Time taken for tests: 0.046 seconds Complete requests: 100 Failed requests: 0 Total transferred: 11100 bytes HTML transferred: 1000 bytes Requests per second: 2161.69 [#/sec] (mean) Time per request: 46.260 [ms] (mean) Time per request: 0.463 [ms] (mean, across all concurrent requests) Transfer rate: 234.32 [Kbytes/sec] received
Ruby
Concurrency Level: 100 Time taken for tests: 0.007 seconds Complete requests: 100 Failed requests: 0 Total transferred: 5300 bytes HTML transferred: 1100 bytes Requests per second: 14341.03 [#/sec] (mean) Time per request: 6.973 [ms] (mean) Time per request: 0.070 [ms] (mean, across all concurrent requests) Transfer rate: 742.26 [Kbytes/sec] received
「Requests per second」が1秒間に処理されたリクエスト数なので、この数値が大きい方がパフォーマンスがよいことになります。
意外にも一番よいスコアーだったのはRubyでした。
僅差でGo、Node.jsは1桁低いスコアーでした。
今回用意した検証コードがI/OよりCPUリソースが必要だったのか、シングルスレッドのNode.jsにとっては不利なものだったのかもしれません。
同時100接続ではRubyのスコアーがよかったですが、同時2000接続では、Rubyは応答できませんでした。
また、Goのスコアー7329.66に対してNode.jsのスコアーは3914.49と差が小さくなっていました。
Node.jsはアクセスが多い場合に力を発揮するようです。
比較2
比較1で使用したabコマンドでの同時接続数を100づつ増やして上限を確認してみました。
結果2
Go
同時2300までOK、2400でエラー
Node.js
同時2100までOK、2200でエラー
Ruby
同時120までOK、130でエラー
同時接続数はGoが一番よい結果が出ました。
Node.jsもGoに続くよい結果でした。
Rubyが少なすぎる気がしたのでwebrickでも試してみましたが、同じような結果でした。
まとめ
応答速度、同時アクセス数とも、Goのパフォーマンスが優れています。 通信回数の多いゲーム等を開発する場合は、バックエンドにGoを使うとよいかもしれません。
あと、処理内容にもよると思いますが、同時100アクセスくらいならRubyで十分かもしれません。