私は仕事で Node.js を使用していますが、非常に強力であることがわかりました。Node.js を説明する言葉を 1 つ選ぶことを余儀なくされましたが、私は「興味深い」と言いたいです (これは純粋に肯定的な形容詞ではありません)。コミュニティは活発で成長しています。JavaScript は、その奇妙な点にもかかわらず、コーディングするのに最適な言語になる可能性があります。また、「ベスト プラクティス」と適切に構造化されたコードのパターンについての自分の理解を毎日再考することになります。現在、膨大なアイデアのエネルギーが Node.js に流れ込んでおり、そこで作業することで、このすべての思考にさらされます。
本番環境で Node.js を使用することは間違いなく可能ですが、ドキュメントで約束されているように見える「ターンキー」展開とはかけ離れています。Node.js v0.6.x では、"クラスター" がプラットフォームに統合され、重要な構成要素の 1 つが提供されていますが、私の "production.js" スクリプトは、ログの作成などを処理するための 150 行のロジックのままです。ディレクトリ、死んだワーカーのリサイクルなど。「深刻な」本番サービスの場合、着信接続を抑制し、Apache がPHPに対して行うすべてのことを行う準備も必要です。公平を期すために言うと、Ruby on Railsにはまさにこの問題があります。これは、次の 2 つの補完的なメカニズムによって解決されます。1) Ruby を Rails/Node に配置する。アパッチ/ Lighttd )。Web サーバーは、静的コンテンツの提供、ログへのアクセス、URL の書き換え、SSLの終了、アクセス ルールの適用、複数のサブサービスの管理を効率的に行うことができます。実際のノード サービスにヒットするリクエストの場合、Web サーバーはリクエストをプロキシします。2)ワーカー プロセスを管理し、定期的にリサイクルするUnicornのようなフレームワークを使用する。存在する可能性がありますが、まだ見つけられておらず、手作業で作成した「production.js」で約150行を使用しています。
Expressのようなフレームワークを読むと、1 つの何でも屋の Node.js サービスを通じてすべてを提供するのが標準的な方法のように思えます... "app.use(express.static(__dirname + '/public'))" . 低負荷のサービスと開発の場合は、おそらく問題ありません。しかし、サービスに大きな負荷をかけ、24 時間年中無休で実行しようとするとすぐに、Nginxがサイトの前面に配置され、すべてを処理するように、大規模なサイトに十分に焼き付けられた、強化された C コードを要求する動機がすぐにわかります。静的コンテンツ リクエスト (... Amazon CloudFrontなどのCDNを設定するまで))。これに関するややユーモラスで恥ずかしがらずに否定的な見方については、この男を参照してください。
Node.js は、サービス以外の用途もますます見つけています。Web コンテンツを提供するために別のものを使用している場合でも、Node.js をビルド ツールとして使用し、npmモジュールを使用してコードを整理し、Browserifyを使用して単一のアセットにつなぎ合わせ、uglify-jsをデプロイ用に縮小する場合があります。 . Web を扱う場合、JavaScript は完全なインピーダンス マッチングであり、多くの場合、最も簡単な攻撃経路になります。たとえば、大量のJSON応答ペイロードを処理したい場合は、構造化データのユーティリティ ベルトである私のunderscore-CLIモジュールを使用する必要があります。
長所短所:
- 長所: サーバー担当者にとって、バックエンドで JavaScript を記述することは、最新の UI パターンを学習するための「ゲートウェイ ドラッグ」でした。クライアント コードを書くのが怖くなくなりました。
- 長所: 適切なエラー チェックを奨励する傾向があります (事実上すべてのコールバックによってエラーが返され、プログラマーがそれを処理するようにしつこく要求されます。また、async.js およびその他のライブラリは、「これらのサブタスクのいずれかが失敗した場合は失敗する」パラダイムを、典型的な同期コードよりもはるかにうまく処理します。 )
- 長所: 進行中のタスクのステータスの取得、ワーカー間の通信、キャッシュ状態の共有など、興味深いタスクや通常は難しいタスクが簡単になります。
- 長所: 堅牢なパッケージ マネージャー (npm) に基づく巨大なコミュニティと多数の優れたライブラリ
- 短所: JavaScript には標準ライブラリがありません。機能のインポートに慣れすぎて、JSON.parse や、npm モジュールの追加を必要としないその他の組み込みメソッドを使用すると、違和感を覚えます。これは、すべてに 5 つのバージョンがあることを意味します。Node.js の「コア」に含まれるモジュールでさえ、デフォルトの実装に不満がある場合に備えて、さらに 5 つのバリアントがあります。これは急速な進化につながりますが、ある程度の混乱ももたらします。
シンプルなリクエストごとに 1 プロセスのモデル ( LAMP ) との比較:
- 長所: 何千ものアクティブな接続に拡張可能です。非常に高速で非常に効率的です。Web フリートの場合、必要なボックスの数が PHP や Ruby と比べて 10 分の 1 に減少する可能性があります。
- 長所: 並列パターンを書くのは簡単です。Memcachedから 3 つ (または N 個) の blob をフェッチする必要があるとします。PHP でこれを行います...最初の blob、次に 2 番目、3 番目の blob を取得するコードを記述しましたか? うわー、それは遅いです。Memcached の特定の問題を修正するための特別なPECLモジュールがありますが、データベース クエリと並行して Memcached データを取得したい場合はどうすればよいでしょうか? Node.js では、パラダイムが非同期であるため、Web 要求で複数のことを並行して実行することは非常に自然です。
- 短所: 非同期コードは基本的に同期コードよりも複雑であり、同時実行が実際に何を意味するのかを十分に理解していない開発者にとって、事前学習曲線は困難な場合があります。それでも、ロックを使用してあらゆる種類のマルチスレッド コードを記述するよりもはるかに簡単です。
- 短所: 計算集約型のリクエストがたとえば 100 ミリ秒実行されると、同じ Node.js プロセスで処理されている他のリクエストの処理が停止します ... 別名、協調マルチタスク。これは、Web ワーカー パターン (コストのかかるタスクを処理するためにサブプロセスをスピンオフする) で軽減できます。あるいは、多数の Node.js ワーカーを使用して、それぞれが 1 つのリクエストを同時に処理できるようにすることもできます (プロセスのリサイクルがないため、それでもかなり効率的です)。
- 短所: 本番システムの実行は、Apache + PHP、Perl、RubyなどのCGIモデルよりもはるかに複雑です。未処理の例外はプロセス全体を停止させ、失敗したワーカーを再起動するロジックが必要になります ( clusterを参照)。バグのあるネイティブ コードを含むモジュールは、プロセスをハード クラッシュさせる可能性があります。ワーカーが停止するたびに、それが処理していたすべてのリクエストが破棄されるため、1 つのバグのある API が他の共同ホスト API のサービスを簡単に低下させる可能性があります。
Java / C# / C (C? 本当に?) で「実際の」サービスを作成することと比較
- 長所: Node.js で非同期処理を行うことは、他の場所でスレッド セーフを行うよりも簡単であり、間違いなく大きなメリットがあります。Node.js は、私が今まで取り組んできた非同期パラダイムの中で、最も負担が少ないパラダイムです。優れたライブラリを使用すると、同期コードを記述するよりもわずかに難しくなります。
- 長所: マルチスレッドやロックのバグはありません。確かに、ブロッキング操作のない適切な非同期ワークフローを表現する、より詳細なコードを記述することに前もって投資します。そして、いくつかのテストを作成し、それを機能させる必要があります (これはスクリプト言語であり、ファット フィンガリング変数名は単体テスト時にのみ検出されます)。しかし、いったんそれが機能すると、ハイゼンバグ(100 万回の実行に 1 回しか現れない奇妙な問題) の表面積は、はるかに小さくなります。Node.js コードを作成するための税金は、コーディング フェーズに大きく先行しています。その後、安定したコードになる傾向があります。
- 長所: JavaScript は、機能を表現するのにはるかに軽量です。これを言葉で証明するのは難しいですが、JSON、動的型付け、ラムダ表記、プロトタイプの継承、軽量モジュールなど、何でも同じアイデアを表現するのに必要なコードが少なくなる傾向があります。
- 短所: Java でサービスをコーディングするのが本当に好きなのかもしれません。
JavaScript と Node.js に関する別の視点については、Java 開発者が Node.js を学習した感想と経験に関するブログ投稿、From Java to Node.js をご覧ください。
モジュール
ノードを検討するときは、JavaScript ライブラリの選択によってエクスペリエンスが定義されることに注意してください。ほとんどの人は、非同期パターン ヘルパー (Step、Futures、Async) と JavaScript シュガー モジュール ( Underscore.js )の少なくとも 2 つを使用します。
ヘルパー / JavaScript シュガー:
- Underscore.js - これを使用します。早くやれよ。_.isString() や _.isArray() などを使用すると、コードが読みやすくなります。そうでなければ安全なコードを書く方法がよくわかりません。また、強化された command-line-fu については、私自身のUnderscore-CLIを確認してください。
非同期パターン モジュール:
- ステップ- シリアル アクションとパラレル アクションの組み合わせを表現する非常に洗練された方法。個人的なおすすめです。ステップコードがどのように見えるかについての私の投稿を参照してください。
- Futures - より柔軟な (それは本当に良いことでしょうか?) 要件によって順序付けを表現する方法です。「a、b、c を並行して開始する。A と B が終了したら AB を開始する。A と C が終了したら AC を開始する」などの表現ができます。このような柔軟性には、ワークフローのバグを回避するための注意が必要です (コールバックを呼び出さない、複数回呼び出すなど)。先物の使用に関するRaynos の投稿を参照してください(これは、私が先物を「取得」するきっかけとなった投稿です)。
- Async - パターンごとに 1 つのメソッドを持つ、より伝統的なライブラリ。ステップへの改宗とその後の Async のすべてのパターンは、単一のより読みやすいパラダイムを使用してステップで表現できることに気付く前に、これから始めました。
- TameJS - OKCupid によって作成された、直列および並列ワークフローをエレガントに記述するための新しい言語プリミティブ「await」を追加するプリコンパイラです。パターンは素晴らしく見えますが、事前コンパイルが必要です。私はまだこれについて決心しています。
- StreamlineJS - TameJS のライバル。私はテイムに傾いていますが、あなたは自分で決めることができます.
または、非同期ライブラリのすべてを読むには、この著者とのパネル インタビューを参照してください。
ウェブ フレームワーク:
- Web サイトを整理するための優れた Ruby on Rails-esk フレームワークを表現します。JADEを XML/HTML テンプレート エンジンとして使用しているため、HTML の作成がはるかに簡単になり、エレガントにさえなります。
- jQuery技術的にはノード モジュールではありませんが、jQuery は急速にクライアント側ユーザー インターフェイスのデファクト スタンダードになりつつあります。jQuery は、一連の DOM 要素を「クエリ」するための CSS に似たセレクターを提供します。これらの要素を操作できます (セット ハンドラー、プロパティ、スタイルなど)。同様に、Twitter のBootstrap CSS フレームワーク、MVCパターン用のBackbone.js 、すべての JavaScript ファイルを 1 つのファイルにまとめる Browserify.jsがあります。これらのモジュールはすべて事実上の標準になりつつあるため、聞いたことがない場合は少なくともチェックする必要があります。
テスト:
- JSHint - 使用する必要があります。最初はこれを使用していませんでしたが、今では理解できないようです。JSLint は、Java のようなコンパイル済み言語で得られる一連の基本的な検証を追加します。括弧の不一致、宣言されていない変数、多くの形状とサイズの型。また、私が「アナルモード」と呼んでいるものをさまざまな形式でオンにすることもできます。ここでは、空白などのスタイルを確認できます。それがあなたの好みであれば問題ありませんが、本当の価値は、正確な行番号について即座にフィードバックを得ることです終了の ")" を忘れてしまいました...コードを実行して問題のある行にヒットする必要はありません。「JSHint」は、Douglas CrockfordのJSLintのより構成可能なバリアントです。
- 私が好むようになっている誓いのモカの競争相手。どちらのフレームワークも基本を十分に処理しますが、複雑なパターンは Mocha の方が表現しやすい傾向があります。
- Vows Vows は非常にエレガントです。そして、どのテストケースが成功したか失敗したかを示す素敵なレポート (--spec) を出力します。30 分かけて学習すれば、最小限の労力でモジュールの基本的なテストを作成できます。
- Zombie -仮想「ブラウザ」としてJSDomを使用した HTML と JavaScript のヘッドレステスト. 非常に強力なもの。これをReplayと組み合わせて、ブラウザー内コードの非常に高速な決定論的テストを取得します。
- テストについて「考える」方法についてのコメント:
- テストはオプションではありません。JavaScript のような動的言語では、静的チェックはほとんどありません。たとえば、4 を予期するメソッドに 2 つのパラメーターを渡しても、コードが実行されるまで中断されません。JavaScript でバグを作成するためのハードルはかなり低いです。コンパイル済み言語との検証のギャップを埋めるには、基本的なテストが不可欠です。
- 検証を忘れて、コードを実行するだけです。すべてのメソッドについて、私の最初の検証ケースは「何も壊れない」であり、それが最も頻繁に発生するケースです。スローせずにコードが実行されることを証明すると、バグの 80% がキャッチされ、コードの信頼性が大幅に向上するため、スキップした微妙な検証ケースを戻って追加することになります。
- 小さく始めて、慣性障壁を破ります。私たちはみんな怠け者で、時間に追われており、テストを「余分な作業」と見なしがちです。だから、小さく始めてください。テスト ケース 0 を書きます - モジュールをロードし、成功を報告します。これだけのことを自分に強いると、テストに対する慣性の壁が破られます。ドキュメントを読むことを含め、初めて実行するのに 30 分未満です。ここで、テスト ケース 1 を作成します。メソッドの 1 つを呼び出して、「何も壊れない」こと、つまり、エラーが返されないことを確認します。テスト ケース 1 の所要時間は 1 分未満です。慣性がなくなると、テスト範囲を段階的に拡大することが容易になります。
- コードを使用してテストを進化させます。模擬サーバーなどを使用した「正しい」エンド ツー エンド テストがどのように見えるかについて怖がらないでください。コードは単純なものから始まり、新しいケースを処理するように進化します。テストもすべきです。コードに新しいケースと新しい複雑さを追加したら、テスト ケースを追加して新しいコードを実行します。バグを見つけたら、検証や新しいケースを追加して、欠陥のあるコードをカバーします。デバッグ中にコードの一部に自信が持てなくなった場合は、戻ってテストを追加し、思ったとおりに動作していることを証明します。サンプル データの文字列を (呼び出した他のサービス、スクレイピングした Web サイトなどから) キャプチャし、解析コードにフィードします。ここにいくつかのケースがあり、そこで検証が改善され、信頼性の高いコードが得られます。
また、推奨される Node.js モジュールの公式リストも確認してください。ただし、GitHub の Node Modules Wikiはより完全で優れたリソースです。
Node を理解するには、いくつかの重要な設計上の選択肢を検討することが役立ちます。
Node.js はイベント ベースであり、非同期/ノンブロッキングです. 着信 HTTP 接続などのイベントは、JavaScript 関数を起動して、データベースへの接続や別のサーバーからのコンテンツのプルなどの他の非同期タスクを開始します。これらのタスクが開始されると、イベント関数が終了し、Node.js はスリープ状態に戻ります。データベース接続が確立されたり、外部サーバーがコンテンツに応答したりするとすぐに、コールバック関数が起動し、さらに多くの JavaScript コードが実行され、さらに多くの非同期タスク (データベース クエリなど) が開始される可能性があります。このように、Node.js は複数の並列ワークフローのアクティビティを喜んでインターリーブし、任意の時点でブロックされていないアクティビティを実行します。これが、Node.js が何千もの同時接続を管理する素晴らしい仕事をする理由です。
他のみんなと同じように、接続ごとに 1 つのプロセス/スレッドを使用しないのはなぜですか?Node.js では、新しい接続は非常に小さなヒープ割り当てです。新しいプロセスをスピンアップすると、かなり多くのメモリ (一部のプラットフォームではメガバイト) が必要になります。しかし、実際のコストは、コンテキスト切り替えに関連するオーバーヘッドです。10^6 のカーネル スレッドがある場合、カーネルは次に実行するスレッドを決定するために多くの作業を行う必要があります。Linux 用の O(1) スケジューラを構築するために多くの作業が行われましたが、最終的には、10^6 プロセスが CPU 時間を競合するよりも、単一のイベント駆動型プロセスを使用する方がはるかに効率的です。また、過負荷状態では、マルチプロセス モデルの動作が非常に悪く、重要な管理および管理サービス、特に SSHD が不足します (つまり、ボックスにログインして、実際にどれだけ問題があるかを把握することさえできません)。
Node.js はSINGLE THREADEDであり、LOCK FREEです。Node.js は、非常に慎重な設計の選択により、プロセスごとに 1 つのスレッドしかありません。このため、複数のスレッドが同時にデータにアクセスすることは基本的に不可能です。したがって、ロックは必要ありません。スレッドは難しいです。本当に大変です。それが信じられない場合は、スレッド化されたプログラミングを十分に行っていません。ロックを正しく行うのは難しく、追跡が非常に困難なバグが発生します。ロックとマルチスレッドを排除することで、最も厄介な種類のバグの 1 つがなくなります。これは、ノードの最大の利点の 1 つかもしれません。
しかし、16 コア ボックスをどのように活用すればよいでしょうか?
ふたつのやり方:
- 画像エンコーディングのような大きな負荷のかかる計算タスクの場合、Node.js は子プロセスを起動したり、追加のワーカー プロセスにメッセージを送信したりできます。この設計では、イベントの流れを管理する 1 つのスレッドと、負荷の高い計算タスクを実行し、他の 15 個の CPU を処理する N 個のプロセスがあります。
- Web サービスのスループットをスケーリングするには、クラスターを使用して、コアごとに 1 つのボックスで複数の Node.js サーバーを実行する必要があります(Node.js v0.6.x では、ここにリンクされている公式の「クラスター」モジュールが、learnboost バージョンを置き換えます。別の API)。これらのローカル Node.js サーバーは、新しい接続を受け入れるためにソケット上で競合し、それらの間で負荷を分散できます。接続が受け入れられると、これらの共有プロセスの 1 つに緊密にバインドされます。理論的には、これは悪いように思えますが、実際には非常にうまく機能し、スレッドセーフなコードを書くという頭痛の種を避けることができます。また、これは Node.js が優れた CPU キャッシュ アフィニティを取得し、メモリ帯域幅をより効果的に使用することを意味します。
Node.js を使用すると、汗をかくことなく非常に強力なことを実行できます。さまざまなタスクを実行し、 TCPポートでコマンドをリッスンし、いくつかの画像をエンコードするNode.js プログラムがあるとし5 行のコードで、アクティブなタスクの現在のステータスを表示する HTTP ベースの Web 管理ポータルを追加できます。これは簡単です:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(myJavascriptObject.getSomeStatusInfo());
}).listen(1337, "127.0.0.1");
これで、URL にアクセスして、実行中のプロセスのステータスを確認できます。ボタンをいくつか追加すると、「管理ポータル」が完成します。実行中の Perl / Python / Ruby スクリプトがある場合、「管理ポータルを投入する」だけでは簡単ではありません。
しかし、JavaScript は遅い / 悪い / 悪 / 悪魔のようではありませんか? JavaScript には奇妙な点がいくつかありますが、"良い部分" を備えた非常に強力な言語があり、いずれにせよ、JavaScript はクライアント (ブラウザー) の言語です。JavaScript は定着しています。他の言語はそれを IL としてターゲットにしており、世界クラスの才能が最先端の JavaScript エンジンを作成するために競い合っています。ブラウザーにおける JavaScript の役割のために、JavaScript を非常に高速にするために膨大な量のエンジニアリング作業が投入されています。 V8少なくとも今月は、最新かつ最高の JavaScript エンジンです。効率性と安定性の両方で他のスクリプト言語を圧倒します (Ruby のことを見てください)。そして、Microsoft、Google、Mozilla の大規模なチームがこの問題に取り組み、最高の JavaScript エンジンを構築するために競争することで、より良くなるだけです (最新のエンジンはすべて大量のJITを実行するため、もはや JavaScript の「インタープリター」ではありません)。1 回限りのコードのフォールバックとしてのみ解釈して内部でコンパイルします)。ええ、私たちは皆、奇妙な JavaScript 言語の選択をいくつか修正できればと願っていますが、実際にはそれほど悪くはありません。この言語は非常に柔軟であるため、実際には JavaScript をコーディングするのではなく、Step や jQuery をコーディングしています。JavaScript では、他のどの言語よりもライブラリがエクスペリエンスを定義します。Web アプリケーションを構築するには、とにかく JavaScript の知識がほとんど必要なので、サーバー上で JavaScript を使用してコーディングすると、一種のスキルセットの相乗効果があります。クライアントコードを書くのが怖くなくなりました。
さらに、JavaScript が本当に嫌いな場合は、CoffeeScriptのようなシンタックス シュガーを使用できます。または、 Google Web Toolkit (GWT)など、JavaScript コードを作成するその他のもの。
JavaScript といえば、「クロージャ」とは何ですか? - コール チェーン全体でレキシカル スコープの変数を保持する、というかなり派手な言い方です。;) このような:
var myData = "foo";
database.connect( 'user:pass', function myCallback( result ) {
database.query("SELECT * from Foo where id = " + myData);
} );
// Note that doSomethingElse() executes _BEFORE_ "database.query" which is inside a callback
doSomethingElse();
「myData」をオブジェクトに格納するなどの厄介なことをせずに使用できる方法をご覧ください。Java とは異なり、"myData" 変数は読み取り専用である必要はありません。この強力な言語機能により、非同期プログラミングがより冗長になり、負担が軽減されます。
非同期コードの作成は、単純なシングル スレッド スクリプトの作成より常に複雑になりますが、Node.js を使用すると、それほど難しくなく、数千の同時接続に対する効率とスケーラビリティに加えて、多くの利点が得られます。 ..