hakobera's blog

技術メモ。たまに雑談

Node.js vs Play vs SAStruts

前置き

Experiences with Node.js: Porting a RESTful Service Written in Java - ZiggyTech

上記記事では、実験的にJava (Jersey + Hibernate on Tomcat) で実装された REST API サーバを Node.js で書きなおしてみたら、少ないリソース(CPU/メモリ使用量)でほぼ同等のパフォーマンスが出せたよ(ただし、O/Rマッパーを使用しない場合)、と書いてあります。この件に関して @koichik さんとやり取りしていた中で以下のような意見を頂いたので、実際にやってみましたという記事です。

さすがに素の Struts 1.x は書き方も忘れたので、日本では結構採用事例があると思われる SAStruts と、最近仕事で使っている Play framework 1.2 を比較対象にします。

比較内容

/orders/ へのアクセスがあると、データベースに50件分のデータを検索しにいき、JSON の配列データに変換して返す REST APIスループット、および、その際の CPUとメモリ使用量を計測します。

実験に使ったソースは github においてあります。
hakobera/node-vs-sastruts-vs-playframework-rest-api-bench · GitHub

計測マシンのスペック

MacBook Pro 2011 early

計測対象の組み合わせ

  • Node.js
    • Node.js v0.6.12
    • express 2.5.8
    • pg 0.6.13 (JavaScript Pure Driver / SQL 直接実行, O/Rマッパーなし)
  • Play
    • Play framework 1.2.4

Java でGC が極端に発生しないようにヒープは最大 512MB を設定、何回は試行後、ヒープサイズが安定してから計測。それ以外はスレッドプールなどはデフォルト設定。全てプロダクションモードでの実行結果。

計測方法

元の記事では JMeter を使っていますが、JMeter だとそれ自体が結構リソースを食うので、今回は Apache Bench を利用しました。

ab -c 64 -n 5000 http://127.0.0.1:8080/orders/

スループットは上記コマンドを5回実行して、最大値と最小値を除く3回の平均値を採用。CPU 使用率とメモリ使用量はアクティビティモニタによる目測。

結果

スループット CPU 使用率 メモリ使用量 スレッド数
Node.js 488 (req/sec) 100.6 (%) 55.6 (MB) 2
Node.js (2 cluster) 739 (req/sec) 200.5(100.5 + 100.0) (%) 115.7 (57.6+58.1) (MB) 12(6*2)
Node.js (3 cluster) 981 (req/sec) 300.7(100.4 + 100.2 + 100.1) (%) 167.1 (56.4+55.4+55.3) (MB) 18(6*3)
Node.js (4 cluster) 1129 (req/sec) 400.8(100.0 + 100.3 + 100.3 + 100.2) (%) 222.6 (55.6 + 55.4 + 55.7 + 55.9) (MB) 24 (6*4)
SAStruts 2237 (req/sec) 388.6 (%) 392.6 (MB) 98
Play 906 (req/sec) 557.8 (%) 365.9 (MB) 53

使っているものが色々と違うので直接は比較できないのですが、なんか元の記事とは傾向が異なる感じなりました。

【追記】github で cluster 化の プルリクエスト をもらったので、cluster のベンチを追加しました。4 cluster 以上は DBアクセスがネックになって、スループットは速くなったり、遅くなったりしてました。環境によっては 3,000 req/sec くらいまで伸びるようです。

まとめ

  • Node.js
    • Node.js はシングルインスタンスなので、1コアしかCPUをつかっていないので、CPU は使っても 100%
    • メモリ使用量が圧倒的に少ない
    • 1スレッド辺りのスループットは最も高い。cluster を使えば、他の2つに追いつけるかも(要検証)
  • Play
    • スループットはそこそこ。Node.js よりは速いが、SAStruts には及ばない
    • CPU 使用率が高い
      • デフォルト設定では(コア数+1)スレッドプールとして確保される
      • スレッドプールの設定で上限を下げることは可能。実運用時はスループットとの兼ね合いで調整する必要あり
      • 試しにスレッドプールを1に設定すると、スループットは 304 req/sec まで落ちるが、CPU使用率: 105% になる
  • 補足
    • CPUコア数の多いマシンでは、シングルインスタンスの Node.js が、複数コアを利用できる SAStruts, Play に対しては圧倒的に遅い
    • 2コアの MacBook Air で試してみると、SAStruts >> Play >= Node.js くらいにはなる
    • Node.js は cluster での絶対性能の向上は図れると思うが、他の2つがデフォルトで複数コアを利用できるのに比べて、運用負荷がかかる

感想

  • Node.js は決して遅くはないが O/Rマッパーを使わないようにしても、RDB へのアクセスを伴うアプリケーションでは絶対性能はまだ Java の方が上。(ただし、これが localhost かつ、HTTP サーバを介さないベンチマークであるので、実アプリでの性能ではないことには注意)
  • SAStruts + S2JDBC は総じて優秀
  • (キャッシュ効きまくっているはずなのに)Hibernate 遅い ので、引きずられて Play がそんなに速くならない
    • とはいえすごく遅いわけではない。元記事の Jersey + Hibernate よりはスループット出ているので
    • Play + S2JDBC とか、Play + Doma とかだと、また違った結果になるかも