Hatena::Diary

自分の感受性くらい RSSフィード Twitter

2011-02-20

Node.jsについてのよくある誤解

Node.jsは普及しそうに無い: ニュースの社会科学的な裏側

http://anlyznews.blogspot.com/2011/02/nodejs.html

という記事があったので、もしかしたらいい感じにNode.jsについてのよくある誤解がここに網羅されてるんじゃないかと思ってエントリを書きます。決して顔真っ赤にして反論してるわけじゃないよ!

で、まず

理由は簡単で、Googleがリリースしたアプリケーションサーバーであるnode.jsが、JavaScriptとその実行エンジンを基本とした構成となっており、その性能が高いからだ。

とありますが、Node.jsGoogle製ではありません。Node.jsがベースとしているJavaScriptV8エンジンはGoogle製(のオープンソース)ですが、Node.js自身はJoyentという会社のサポートによって作られています。メインコミッタのRyan DahlもJoyentの社員です。

修正されてました!

あとは、

9. node.jsの問題点

高負荷時のパフォーマンスが優れているのは確かのようだが、現状では次のような欠点もある。万能薬的な使い方は出来ないようだ。

この項目について書けばいいと思うので各指摘点について書いていきます。っていうか、どのような言語であれどのようなフレームワークであれ得手不得手(もしくは適材適所)というものがあるので、万能薬は存在しないというのは一般的な共通認識ということでいいんじゃないのかな。Node.jsの開発者でNode.jsは万能だと言ってる人は誰一人みたことがないですし。

1. イベントループ・モデルで処理を直列化しているので、あるリクエストの処理に時間がかかる場合、他の全てのリクエストをブロックする可能性がある。

まず、この処理というのがI/Oを伴うものだと仮定すると、Node.jsI/Oを非同期で行うためブロックは発生しません。他の非同期フレームワークであるTwisted(Python)やEventMachine(Ruby)などでは、組み合わせて使用するその言語のライブラリ次第でブロックが発生してしまうことがありますが、Node.jsは非同期であることが前提の環境なのでライブラリで同期I/Oが使われていてそれを同期呼び出しするようなケースはほぼありません*1

I/Oを伴わない部分だとすると、そのように時間のかかる処理(CPUインテンシブな処理)には向かないと言えます。ただ、一般的なWebアプリケーションであればI/O処理よりもCPU処理に時間のかかるケースというのはほとんどないのではないでしょうか。CPUがL1/L2キャッシュやRAMのみを用いて処理している場合とHDDネットワークを用いる場合との差は、以下のエントリに端的に表現されています。

このブロックしてしまう処理というのはI/Oに端的に表れていて、たとえばCPUのL1キャッシュだと3サイクル、L2で14、RAMだと250で済むんだけど、それがDISKになると41,000,000サイクルかかって、ネットワークならさらに240,000,000サイクルもかかってしまうんだ。これがどのくらいすごいのかというと、サイクルをメートル換算してみるとわかりやすい。RAMでも250メートルでまだ目の届く範囲なんだけど、DISKになった瞬間、地球一周分、ネットワークだとさらに地球6周分!!!このコストの比はやっぱりトンデモナイんだよね。

http://d.hatena.ne.jp/badatmath/20101020/1287587240

あとはまあ、子プロセス作ってCPUインテンシブな処理はそっちにさせるってのも手ですね。

2. イベントループ・モデルで平行処理を行わないため、複数コアを持つCPU/MPU、複数のCPUを使うSMPでパフォーマンスの上昇が、JavaScriptのコード部分は期待できない。

これに関してもそういう理解をしている人は多いと思いますし、実際にNode.jsを1プロセスだけ立ち上げてる場合ではその通りです。しかし、Node.jsには複数の子プロセスを起動してそれらに処理を分散させるための様々なツール(ライブラリ)が既に存在し、使用実績もあります。例えば、fugue, Spark2, clusterなどです。これらを使えばコアの数だけプロセスを立ち上げ、リクエストをそのプロセスにロードバランスするみたいなことが簡単にできます。セッションの共有とかめんどいんじゃないかって思われるかもしれませんが、ExpressのようなConnectベースのWebアプリケーションであれば、connect-redisを使うなどすれば2行追加するだけでセッションの共有ができるようになります。

3. JavaScriptプログラミング言語としての限界が影響する。つまり、オブジェクト指向が不完全であること、動的型付けでJavaC#に対して速度面に限界があること等が、大規模で複雑なアプリケーションには影響を及ぼす可能性がある。

オブジェクト指向が不完全というあたりはちょっとよく分かりませんね。動的型付けで速度面に限界があることが大規模で複雑なアプリケーションに影響を及ぼすというのもよくわかりません。

どちらかというと、JavaScriptプロトタイプベースのオブジェクト指向が、JavaC#などのクラスベースのオブジェクト指向しかやってこなかった人にとって慣れるのに時間がかかるとか、そういった問題はあるかとおもいます。また、動的型付けなのでIDEの支援が受けにくいとか多人数による開発であればきちんとルールを決めておかないと混乱を巻き起こす可能性があるとかそういったことはあるでしょう。ただ、前者についてはJavaScriptに慣れてる開発者も多いでしょうし、後者については他の動的言語でも大規模開発の実績があるのだから乗り越えられる障壁なんだと思っています。

4. あるページのJavaScriptの文法エラーが、サーバーの停止を引き起こす。簡単なプログラム・ミスが、アプリケーションの非クリティカルな部分に発生したとしても、システム全体がダウンする結果となる。

これはひどい誤解で、クライアント側の(HTMLから呼ばれる)JavaScriptが間違っていたからといって、サーバ側のNode.jsプロセスが止まるなんてことはありません。それとも僕が誤読してるのかな?

追記:誤読してたっぽい。あるページの処理を行うサーバ側のJavaScriptでエラーが発生した場合のことだったみたいです。これも下のコメントに書かれているとおり、適切なエラー処理を行っていればNode.jsプロセス全体がダウンするなんてことはありません。

5. 非同期化によるパフォーマンス向上がイベントループ・モデルに依存するため、イベントを受けるコールバック関数が多くなり(単純なファイル操作でも3回コールバックが発生する)、ソースコードの見通しが悪くなる。

これもよくある指摘なんですが、様々な対処方があります。JavaScript自体に関しても、Future(Promise)を使ったパターンやDeferredを使ったパターンなどのライブラリも充実していますし、Node.jsについてもasync.jsなどの使い勝手のいいライブラリが数多く存在します(ちょっと前のエントリだけどhttp://d.hatena.ne.jp/koichik/20100926#1285502400参照)。

6. 利用可能ライブラリが限定的であり、開発支援ツールが無い。Apacheモジュールも種類や用途が多いし、JavaPHPにも覚えきれないほどのフレームワーク製品群とライブラリがある。これらに依存しない用途にしか、現状では利用できない。

利用可能ライブラリは確かに他言語に比べるとまだまだ少ないかもしれませんが、僕の個人的な感想では現状でもあまり不満はありません。また、既存のJavaScriptライブラリ(例えばjQueryYahoo!UIなど)は簡単にNode.jsから使うことができます。開発支援ツールという意味では、JavaScriptが使えるIDEエディタなら大抵普通にコードは書けますし、Node Inspectorを使えばGUI環境でデバッグすることも可能です。ちなみにCloud9というNode.js製のIDEも存在し、これはローカルにインストールせずにホスティング環境で使うこともできます。

JavaPHPの誕生後1年でどれぐらいのライブラリフレームワークが揃っていたかは知りませんが、生まれたての環境としては十分じゃないかなと思っています。ってか、欲しいのがあれば作ればいいだけだし、逆に言うと作って名を挙げるチャンスがあるとも言えますね。

終わりに

で、まあ上に書いたことは全部今書いてる本にもっと詳しく解説されています。というわけで出版されたら買ってね!という宣伝エントリでした。

*1:100%ではありませんが

uncorrelateduncorrelated 2011/02/20 21:02 拝読しました。大変参考になるエントリで、ぜひこちらからもリンクさせて頂きたいのですが、1点、質問があります。

> しかし、Node.jsには複数の子プロセスを起動してそれらに処理を分散させるための様々なツール(ライブラリ)が既に存在し、使用実績もあります。

これ、スレッドやプロセスが増えて、元の状態に戻りませんか?
また、他の処理をブロックしそうなコードを、プログラマーが恣意的に処理しないといけなかったりしませんか?

koichikkoichik 2011/02/20 21:10 4 の「システム全体がダウンする結果となる」は uncaughtException のリスナを登録してない時のデフォルトの動作を言ってるんじゃないかな.リスナ登録すればいいだけなんだけど,知らない人は多そうだね.

http://nodejs.jp/nodejs.org_ja/api/process.html#event_uncaughtException_

koichikkoichik 2011/02/20 21:16 id:uncorrelated
> スレッドやプロセスが増えて、元の状態に戻りませんか?

コア数分のプロセスを立てるだけなので,コア数×100 以上のプロセス or スレッドを立てるのと同じにはならないでしょう.

> 他の処理をブロックしそうなコード

そもそも Web アプリでレスポンスを返す前に CPU ヘビーで時間のかかる処理はしませんよね.今でもバックグラウンドで処理するのが当たり前でしょ? それなら Node.js でも同じで WebWorker とか使えばいいだけ.
何というか,どんな言語でも簡単に無限ループできるからどの言語も普及しないと言ってるのと変わらないレベルに見える.

uncorrelateduncorrelated 2011/02/20 21:29 プログラマの質にもよりますが、一部のページでCPU負荷が高くなる処理もありえますよね。
いわば「駄目ページ」になるわけですが、言語と言うかアプリケーション・サーバーの作りの問題で、そこが他のページ閲覧に影響しうるところが、node.jsの問題点に感じます。

とはいえ、ご指摘いただいた『例えば、fugue, Spark2, clusterなどです。これらを使えばコアの数だけプロセスを立ち上げ、リクエストをそのプロセスにロードバランスするみたいなことが簡単にできます。』については追記させて頂きます。

丁寧なご批判、ありがとうございました。

t_43zt_43z 2011/02/20 21:32 >> uncorrelatedさん
お、トラックバックが飛ばせないのでどうしようかと思ってたのですが、お目に止まったようでよかったです。

> これ、スレッドやプロセスが増えて、元の状態に戻りませんか?
元の状態というのはC10Kが問題になる状態ということでしょうか。
1プロセス(もしくはスレッド)で1リクエストをさばく既存のサーバとは違い、1プロセスで複数のリクエストをさばけるため、CPUが使いきれてない状態でプロセスを無闇に増やしてもさほど意味はありません。
Node.jsの場合、子プロセスの数はコアの数までにすることがベストプラクティスです。
よって、プロセスやスレッドが多くなりすぎていっぱいいっぱいになってしまうC10K問題が再発するようなことはありません。

> また、他の処理をブロックしそうなコードを、プログラマーが恣意的に処理しないといけなかったりしませんか?
これは、http://uncorrelated.servehttp.com/src/example01.js の例のようなコードでしょうか?
確かにこのような処理が起こり得るのであれば、プログラマは注意してコーディングする必要があるでしょう。別のプロセスで処理させるなり工夫すべきだと思います。

>> koichik
あー、確かに。追記しときます

t_43zt_43z 2011/02/20 21:34 お、書いてる途中になんか完結してたw

uncorrelateduncorrelated 2011/02/20 22:04 >>t_43zさん
まずお知らせなのですが、こちらから「9. node.jsの問題点」の下側に、このページへリンクを貼らせて頂きました。

ご丁寧に回答、ありがとうございます。

> プロセスやスレッドが多くなりすぎていっぱいいっぱいになってしまうC10K問題が再発するようなことはありません。

無闇にプロセスやスレッドを増やすわけではないから、イベントループ・モデルは維持されるわけですね。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/t_43z/20110220/1298202879