「Node.jsは静的コンテンツには向いていない」のか?
この記事は東京Node学園祭2012 アドベントカレンダーの8日目の記事です。
この記事を書こうと思った理由
Node.jsに関するWeb上の記事を読んでいると、「Node.jsは静的コンテンツに弱い」とだけ書いてある記事をよく見かけます。有名なところだと、LinkedInのNode.jsのパフォーマンスに関する10個のTipsの3番目のTipsに"Don't use Node.js for static assets"とばっちり書いてあります。
確かにCDNやNginxに比べれば、Node.jsは静的コンテンツの扱いが遅いとは思います。しかし、それは LinkedIn くらいの超大規模なトラフィックがある場合には問題になるとは思いますが、小〜中規模なサイトでもNginxは必須なほど遅いのでしょうか?512MBしかメモリのないVPSにNginxとNode.jsを入れてやりくりすることがホントに効率的なやり方なのでしょうか?
実は自分自身もちょっと前まで「静的ファイルはNginxに任せる」病にかかっていたのですが、PaaSを使って開発するようになり、そもそもNginxインストールできない状況で、Node.jsで静的ファイル配信もありじゃないかと思い始めてきたので、今回、実際にベンチマークで検証してみようと思った次第です。
比較対象
- Nginx 1.2.4
- Node.js 0.8.12
- Node.js標準のhttp, fsモジュール
- node-static 0.6.4
- 静的コンテンツ用のNPMモジュールとしては古参の部類。インメモリキャッシュしてくれる。
- connect 2.6.0 のstaticミドルウェア
- Express の基盤。一番良く使われていると思ったので採択
- st 0.0.9
- 比較的新しい静的コンテンツ用のNPMモジュール。gzip圧縮を標準でサポート。ETagなどクライアント側でも積極的にキャッシュさせるようにして効率的な静的コンテンツ配信が強く意識されています。
比較方法
- Apache Bench (ab) を利用
$ ab -c 200 -n 100000 <url> # gzipありの場合は以下 $ ab -c 200 -n 100000 -H "Accept-Encoding: gzip,deflate,sdch" <url>
- リクエスト/秒で比較
- 画像(png)とHTMLで比較
- gzip 圧縮ありと圧縮なしを比較
- 一部のベンチマークでは、Nginx は worker数を増やしたり、Node.jsはclusterモジュールを利用してcluster化
環境
OS | CPU | Memory | |
---|---|---|---|
Server | CentOS 6.2 | AMD Phenom II X6 1090T | 16GB |
Client | CentOS 5.8 | AMD Phenom II X6 1090T | 16GB |
Server <-> Client 間は1Gbpsのローカルネットワークで繋がっています。注意点として、この環境だとネットワーク帯域の限界から、15,000リクエスト/秒(=約13億リクエスト/日)あたりで性能が頭打ちになります。
ベンチマーク結果
使用したソースファイルや設定ファイルは Gistにおいてあります。
画像 (png)
Node公式サイトのロゴ (5,081 byte) を取得
Nginx | http | node-static | node-static (cluster x4) | connect | st | st (cluster x4) |
---|---|---|---|---|---|---|
11,419 | 5,243 | 5,568 | 11,546 | 3,007 | 4,547 | 11,876 |
worker数=1で比較するとNgixがダブルスコアで圧勝ですが、 node-static, st に関しては、cluster を使えば Nginxとほぼ同等の速度が出ています。
NodeがNgixよりも遅いのは、CPUの使用率が100%に張り付く、つまりCPUボトルネックで速度がでていない。 ただし、CPU利用率は Nginx より高かったことは注記しておきます。
HTML
Node公式サイトをindex.html (8,550 byte)を取得
gzip 圧縮なし
Nginx | http | node-static | node-static (cluster x4) | connect | connect (cluster x4) | st | st (cluster x4) |
---|---|---|---|---|---|---|---|
12,385 | 4885 | 4,975 | 12,297 | 2,921 | 8,982 | 4,268 | 12,401 |
画像と傾向は同じ。
Total transferred: 877906604 bytes HTML transferred: 855101360 bytes
gzip 圧縮あり
http, node-static, connect は非対応なので省略。(connect.compress()はcluster x4でも遅かったので省略)
Nginx | Nginx (worker x4) | st | st (cluster x4) |
---|---|---|---|
5,543 | 15,975 | 4,425 | 12,232 |
Total transferred: 345644803 bytes HTML transferred: 320434219 bytes
gzipありだと NginxがCPUを100%使いきり、速度が大きく低下しました。worker を増やすことでパフォーマンスは改善できますが、VPSなどのCPUのコア数が少ないサーバでgzipを有効化すると逆にパフォーマンスが低下する可能性があることを示唆しています。stは既にgzipなしの時点でCPUボトルネックが発生していたので、gzipなし、ありで性能差が殆ど無い、という面白い結果になりました。gzip圧縮ありだと転送量がgzip圧縮なしの半分以下となりでネットワークに優しく、全体的なスループットの向上に貢献しそうです。
まとめ
- Node.jsの静的コンテンツの性能は、同一のworker数のNginxの約半分だということがわかりました。たしかに、Nginxに比べれば、Node.jsは静的コンテンツの配信には向いていないようです。とはいっても、4,000リクエスト/秒(=約3.4億リクエスト/日)は捌けるので、小〜中規模のサイトなら十分に捌ききれるレベルです。
- NPMモジュールとしては、st がお勧めです。gzip圧縮ありでも大きなパフォーマンス劣化はなく、(CPU効率は良くないですが)cluster を組めば、絶対性能としてNginx の8割程度の速度を出すことはできます。
- connect の static ミドルウェアはダントツで遅い(つまり、connectを拡張したフレームワークであるexpressも遅い)ので、express を使っているサイトは要注意です。Node.jsが静的コンテンツに向かない、と言われる1つの要因は、この最も有名なNode.jsのフレームワーク express (connect) の staticモジュールが遅いということも原因の1つなんじゃないかと、今回測定して感じました。
「Node.jsは静的コンテンツにむいていない」という言葉だけが独り歩きしていますが、実際に測ってみると同じNode.jsのモジュールでも特徴が出ていて面白かったです。ある程度以上の規模になったらNginxやCDNを使うようにした方が良いというのも事実ですが、噂に惑わされずに、ファクトベースで自分の用途にあっているかどうかを見極めていくのが良いと思います。