Node.jsを使用する理由

この記事は、技術コンサルタントでありNode.jsファンでもあるTomislav Capanからのものです。トミスラフは当初、2013年8月にToptalブログでこれを公開しました。元の投稿はこちらでご覧いただけます。ブログは少し更新されました。以下の主題は、この著者の意見と経験に基づいています。

前書き

JavaScriptの人気の高まりに伴い、多くの変更が行われ、今日のWeb開発の顔は劇的に異なっています。最近、サーバー上およびブラウザ上でJavaScriptを実行することでWeb上でできることは、ほんの数年前には想像し難かったか、FlashやJavaアプレットのようなサンドボックス環境にカプセル化されていました。

Node.jsを掘り下げる前に、スタック全体でJavaScriptを使用する利点を確認してください。これにより、言語とデータ形式(JSON)が統一され、開発者のリソースを最適に再利用できます。これはNode.jsよりもJavaScriptの利点であるため、ここでは詳しく説明しません。ただし、Node.jsをスタックに組み込むことの重要な利点です。

Node.jsは、ChromeのV8 JavaScriptエンジン上に構築されたJavaScriptランタイム環境です。 Node.jsの作成者であるRyan Dahlが、「Gmailのようなアプリケーションに触発された」プッシュ機能を備えたリアルタイムのWebサイトの作成を目指していたことは注目に値します。 Node.jsで、彼は開発者に非ブロッキングのイベント駆動型I / Oパラダイムで作業するためのツールを提供しました。

1つの文で:Node.jsは、websocketを介してプッシュテクノロジーを採用しているリアルタイムWebアプリケーションで輝いています。それについて何がそんなに革命的ですか?さて、ステートレスな要求/応答パラダイムに基づいた20年以上のステートレスWebの後、クライアントとサーバーの両方が通信を開始してデータを自由に交換できるリアルタイムの双方向接続を備えたWebアプリケーションがようやくできました。

これは、クライアントが常に通信を開始する典型的なWeb応答パラダイムとはまったく対照的です。さらに、すべて標準のポート80で実行されるオープンWebスタック(HTML、CSS、JS)に基づいています。

FlashやJavaアプレットという形で何年もこれを持っていると主張する人もいるかもしれませんが、実際には、これらはクライアントに配信されるトランスポートプロトコルとしてWebを使用するサンドボックス環境でした。さらに、それらは分離して実行され、多くの場合、追加の許可などを必要とする非標準ポートで動作していました。

Node.jsは、すべての利点を備えており、独自の利点に依存している多くの著名な企業のテクノロジースタックで重要な役割を果たしています。 Node.js Foundationは、Node.js Foundationのケーススタディページに掲載されている短いプレゼンテーションで、企業がNode.jsを検討する理由についてのすべてのベストシンキングを統合しました。

この投稿では、これらの利点がどのように達成されるかだけでなく、Node.jsを使用する理由と、使用しない理由について、いくつかの古典的なWebアプリケーションモデルを例として使用して説明します。

仕組みは?

Node.jsの主なアイデアは、非ブロッキングのイベント駆動型I / Oを使用して、分散デバイス間で実行されるデータ集約型のリアルタイムアプリケーションに直面しても軽量かつ効率的な状態を維持することです。

一口です。

それが本当に意味することは、Node.jsがWeb開発の世界を支配する銀の弾丸の新しいプラットフォームではないということです。代わりに、特定のニーズを満たすプラットフォームです。そして、これを理解することは絶対に不可欠です。 CPUを集中的に使用する操作にNode.jsを使用したくないことは間違いありません。実際、重い計算に使用すると、その利点のほとんどすべてが無効になります。 Node.jsが本当に優れているのは、高速でスケーラブルなネットワークアプリケーションを構築することです。これは、高いスループットに相当する大量の同時接続を高いスループットで処理できるためです。

内部でどのように機能するかは非常に興味深いです。 Node.jsは、各接続(要求)が新しいスレッドを生成してシステムRAMを占有し、最終的に使用可能なRAMの容量で最大化する従来のWebサービス技術と比較して、非ブロッキングI / Oを呼び出し、数万の同時接続をサポートできるようにします(イベントループで保持)。

*元のブログ投稿から取られた画像。

簡単な計算:各スレッドに付随する2 MBのメモリがある可能性があると仮定すると、8 GBのRAMを搭載したシステムで実行すると、理論上最大4000の同時接続になります(Michael Abernethyの記事「Just what is Node .js?」、2011年にIBM developerWorksで公開されました;残念ながら、この記事はもう利用できません)、およびスレッド間のコンテキスト切り替えのコスト。これは、従来のWebサービス技術で通常対処するシナリオです。それをすべて回避することで、Node.jsは100万以上の同時接続、および60万以上の同時Webソケット接続のスケーラビリティレベルを実現します。

もちろん、すべてのクライアント要求間で単一のスレッドを共有するという問題があり、Node.jsアプリケーションを作成する際の潜在的な落とし穴です。第一に、重い計算はNodeの単一スレッドを詰まらせ、すべてのクライアントに問題を引き起こす可能性があります(これについては後で説明します)。第二に、開発者は、Node.jsインスタンスを終了させる(事実上プログラムをクラッシュさせる)コア(最上位)Node.jsイベントループまで例外がバブリングしないように、本当に注意する必要があります。

表面までバブリングする例外を回避するために使用される技術は、エラーをコールバックパラメーターとして呼び出し元に返します(他の環境のようにエラーをスローするのではなく)。未処理の例外が発生した場合でも、Node.jsプロセスを監視し、クラッシュしたインスタンスの必要な回復を実行するツールが開発されています(おそらく、ユーザーセッションの現在の状態を回復することはできません)。最も一般的なのはForeverモジュールであるか、外部システムツールupstartとmonit、または単にupstartで異なるアプローチを使用しています。

npm:ノードパッケージマネージャー

Node.jsについて議論するとき、絶対に省略すべきでないことの1つは、すべてのNode.jsインストールにデフォルトで付属しているnpmツールを使用したパッケージ管理の組み込みサポートです。 npmモジュールの考え方は、Ruby Gemsの考え方と非常に似ています。バージョンと依存関係の管理を備えた、オンラインリポジトリを介した簡単なインストールで利用できる、公開されて再利用可能なコンポーネントのセットです。

パッケージ化されたモジュールの完全なリストは、npm Webサイトで見つけるか、Node.jsで自動的にインストールされるnpm CLIツールを使用してアクセスできます。モジュールエコシステムはすべての人に開かれており、誰でもnpmリポジトリにリストされる独自のモジュールを公開できます。 npmの簡単な紹介は初心者向けガイドに、モジュールの公開の詳細はnpm Publishing Tutorialにあります。

今日最も便利なnpmモジュールの一部は次のとおりです。

  • express — Express.jsは、Node.js用のSinatraにヒントを得たWeb開発フレームワークであり、今日のNode.jsアプリケーションの大部分の事実上の標準です。
  • hapi — Webおよびサービスアプリケーションを構築するための非常にモジュール式で使いやすい構成中心のフレームワーク
  • connect — ConnectはNode.js用の拡張可能なHTTPサーバーフレームワークであり、ミドルウェアとして知られる高性能な「プラグイン」のコレクションを提供します。 Expressの基本基盤として機能します。
  • socket.ioとsockjs —現在最も一般的な2つのwebsocketコンポーネントのサーバー側コンポーネント。
  • pug(以前のJade)— Express.jsのデフォルトであるHAMLに触発された人気のあるテンプレートエンジンの1つ。
  • mongodbおよびmongojs — Node.jsのMongoDBオブジェクトデータベースにAPIを提供するMongoDBラッパー。
  • redis — Redisクライアントライブラリ。
  • lodash(アンダースコア、lazy.js)— JavaScriptユーティリティベルト。 Underscoreがゲームを開始しましたが、主にパフォーマンスの向上とモジュール化の実装により、2つのカウンターパートのうちの1つによって打倒されました。
  • forever —おそらく、特定のノードスクリプトが継続的に実行されることを保証するための最も一般的なユーティリティです。予期しない障害が発生しても、実稼働環境でNode.jsプロセスを維持します。
  • bluebird —非常に優れたパフォーマンスを備えたフル機能のPromises / A +実装
  • moment —日付を解析、検証、操作、およびフォーマットするための軽量なJavaScript日付ライブラリ。

リストは続きます。本当に便利なパッケージがたくさんあり、すべての人が利用できます(ここでは省略しましたが、不快なことはありません)。

Node.jsの使用場所

チャット

チャットは、最も典型的なリアルタイムのマルチユーザーアプリケーションです。 IRC(当時)から、非標準ポートで実行されている多くの独自のオープンプロトコルを介して、標準ポート80で実行されているwebsocketでNode.jsに今日すべてを実装する機能まで。

チャットアプリケーションは、実際にはNode.jsのスイートスポットの例です。これは、分散デバイス間で実行される、軽量でトラフィックが多く、データ集約型(ただし、処理/計算は少ない)のアプリケーションです。また、シンプルであるため、学習の優れたユースケースでもありますが、典型的なNode.jsアプリケーションで使用するほとんどのパラダイムをカバーしています。

それがどのように機能するかを説明してみましょう。

最も単純なシナリオでは、Webサイトに1つのチャットルームがあり、人々が来て、1対多(実際にはすべて)の方法でメッセージを交換できます。たとえば、ウェブサイトに3人の人がいて、全員が掲示板に接続しているとします。

サーバー側には、2つのことを実装する簡単なExpress.jsアプリケーションがあります。1)メッセージボードと新しいメッセージ入力を初期化するための「送信」ボタンの両方を含むWebページを提供するGET '/'要求ハンドラー2)websocketクライアントによって発行された新しいメッセージをリッスンするwebsocketsサーバー。

クライアント側には、入力メッセージを取得してWebsocketに送信する「送信」ボタンクリックイベント用のハンドラーと、新しい着信メッセージをリッスンするハンドラーが設定されたHTMLページがあります。 websocketsクライアント上(つまり、サーバーがクライアントに表示させたい他のユーザーが送信したメッセージ)。

クライアントの1人がメッセージを投稿すると、次のようになります。

  • ブラウザーはJavaScriptハンドラーを介して「送信」ボタンのクリックをキャッチし、入力フィールド(つまり、メッセージテキスト)から値を取得し、サーバーに接続されたwebsocketクライアントを使用してwebsocketメッセージを送信します(Webページの初期化で初期化されます)。
  • WebSocket接続のサーバー側コンポーネントはメッセージを受信し、ブロードキャスト方式を使用して、接続されている他のすべてのクライアントにメッセージを転送します。
  • すべてのクライアントは、Webページ内で実行されているwebsocketsクライアント側コンポーネントを介してプッシュメッセージとして新しいメッセージを受信します。次に、メッセージの内容をピックアップし、ボードに新しいメッセージを追加することでWebページをその場で更新します。
元のブログからの画像。

これは最も単純な例です。より堅牢なソリューションのために、Redisストアに基づいたシンプルなキャッシュを使用できます。または、より高度なソリューションでは、クライアントへのメッセージのルーティングを処理するメッセージキューと、一時的な接続損失やオフライン中の登録済みクライアントのメッセージを保存できる堅牢な配信メカニズムを使用します。ただし、どのような改善を行ったとしても、Node.jsは同じ基本原則(イベントへの反応、多数の同時接続の処理、ユーザーエクスペリエンスの流動性の維持)で引き続き動作します。

オブジェクトDBの上のAPI

Node.jsはリアルタイムアプリケーションで非常に優れていますが、オブジェクトDB(MongoDBなど)からデータを公開するのに非常に適しています。 JSONに保存されたデータにより、Node.jsはインピーダンスの不一致やデータ変換なしで機能します。

たとえば、Railsを使用している場合、JSONからバイナリモデルに変換し、データがReact.js、Angular.jsなど、または単純なjQuery AJAXによって消費されるときに、HTTPを介してJSONとして公開します呼び出します。 Node.jsを使用すると、JSONオブジェクトをREST APIで公開して、クライアントが使用できるようになります。さらに、データベースから読み取りまたは書き込みを行うときに、MongoDBを使用している場合は、JSONと他のものとの間の変換について心配する必要はありません。つまり、クライアント、サーバー、データベース全体で統一されたデータシリアル化形式を使用することで、複数の変換の必要性を回避できます。

キューに入れられた入力

大量の同時データを受信して​​いる場合、データベースがボトルネックになる可能性があります。上記のように、Node.jsは同時接続自体を簡単に処理できます。ただし、データベースへのアクセスはブロッキング操作であるため(この場合)、問題が発生します。解決策は、データが実際にデータベースに書き込まれる前に、クライアントの動作を確認することです。

このアプローチにより、システムは高負荷の下で応答性を維持します。これは、クライアントが正常なデータ書き込みの確定確認を必要としない場合に特に便利です。典型的な例には、ユーザー追跡データのロギングまたは書き込みがあり、バッチで処理され、後まで使用されません。最終的な整合性(NoSQLの世界でよく使用される)が許容される場合、即座に反映する必要のない操作(Facebookの「いいね」カウントの更新など)も可能です。

データは、何らかの種類のキャッシュまたはメッセージキュー(MQ)インフラストラクチャ(RabbitMQ、ZeroMQなど)を介してキューに入れられ、個別のデータベースバッチ書き込みプロセス、またはそのようなタスクのためのパフォーマンスの高いプラットフォームで記述された計算集中処理バックエンドサービスによってダイジェストされます。同様の動作は、他の言語/フレームワークでも実装できますが、同じハードウェアではなく、同じ高い維持されたスループットで実装できます。

元の記事から撮影した画像。

要するに、Nodeを使用すると、データベースの書き込みを側面にプッシュし、後で処理し、成功したかのように処理できます。

データストリーミング

従来のWebプラットフォームでは、HTTPリクエストとレスポンスは孤立したイベントのように扱われます。実際、実際にはストリームです。 Node.jsでこの観察結果を利用して、いくつかのクールな機能を構築できます。たとえば、データがストリームを介して入力され、オンライン形式で処理できるため、アップロード中のファイルを処理できます。これは、リアルタイムのオーディオまたはビデオのエンコード、および異なるデータソース間のプロキシの場合に実行できます(次のセクションを参照)。

プロキシ

Node.jsは、大量の同時接続を非ブロック方式で処理できるサーバー側プロキシとして簡単に使用できます。さまざまな応答時間でさまざまなサービスをプロキシしたり、複数のソースポイントからデータを収集したりする場合に特に便利です。

例:サードパーティのリソースと通信するサーバー側アプリケーション、さまざまなソースからデータを取り込む、またはサードパーティのクラウドサービスに画像や動画などのアセットを保存することを検討してください。

専用のプロキシサーバーは存在しますが、プロキシインフラストラクチャが存在しない場合、またはローカル開発にソリューションが必要な場合は、代わりにNodeを使用すると便利です。これにより、アセットとプロキシ/スタブAPIリクエスト用のNode.js開発サーバーを使用してクライアント側アプリを構築でき、本番環境では専用のプロキシサービス(nginx、HAProxyなど)との対話を処理できることを意味します。)。

ブローカー—ストックトレーダーのダッシュボード

アプリケーションレベルに戻りましょう。デスクトップソフトウェアが支配的ですが、リアルタイムのWebソリューションに簡単に置き換えることができる別の例は、株価の追跡、計算/技術分析の実行、グラフ/チャートの作成に使用されるブローカーの取引ソフトウェアです。

リアルタイムのWebベースのソリューションに切り替えると、ブローカーはワークステーションや職場を簡単に切り替えることができます。すぐに、フロリダのビーチやイビサ島、バリ島で彼らと会うかもしれません。

アプリケーション監視ダッシュボード

Node-with-web-socketsが完全に適合するもう1つの一般的なユースケース:Webサイトの訪問者を追跡し、リアルタイムで相互作用を視覚化します。ユーザーからリアルタイムの統計情報を収集したり、ファンネルの特定のポイントに到達したときにコミュニケーションチャネルを開いて訪問者とターゲットを絞ったインタラクションを導入することで、次のレベルに移動することもできます。キャンディ

あなたの訪問者がリアルタイムで何をしていたかを知っているなら、あなたのビジネスをどのように改善できるか想像してください-彼らの相互作用を視覚化できれば。 Node.jsのリアルタイムの双方向ソケットを使用して、できるようになりました。

システム監視ダッシュボード

それでは、インフラストラクチャの側面を見てみましょう。たとえば、ユーザーにサービス監視ページ(GitHubステータスページなど)を提供したいSaaSプロバイダーを想像してください。 Node.jsイベントループを使用すると、非同期の方法でサービスのステータスをチェックし、websocketを使用してクライアントにデータをプッシュする強力なWebベースのダッシュボードを作成できます。

このテクノロジーを使用して、社内(社内)および公共サービスの両方のステータスをリアルタイムで報告できます。その考えをもう少し推し進めて、Node.jsとwebsocketsによって支えられたオープンWebスタックで実行される、通信事業者、クラウド/ネットワーク/ホスティングプロバイダー、または何らかの金融機関のアプリケーションを監視するネットワークオペレーションセンター(NOC)を想像してみてくださいJavaやJavaアプレットの代わりに。

注:Node.jsでハードリアルタイムシステム(つまり、一貫した応答時間を必要とするシステム)を構築しようとしないでください。 Erlangは、おそらくそのクラスのアプリケーションに適した選択肢です。

Node.jsを使用できる場所

サーバー側のWebアプリケーション

Express.jsを使用したNode.jsを使用して、サーバー側で従来のWebアプリケーションを作成することもできます。ただし、可能ですが、Node.jsがレンダリングされたHTMLを実行するこのリクエスト/レスポンスパラダイムは、最も一般的なユースケースではありません。このアプローチには賛否両論があります。考慮すべきいくつかの事実を次に示します。

長所:

  • アプリケーションにCPUを集中的に使用する計算がない場合は、MongoDBなどのJSONストレージオブジェクトDBを使用すると、データベースレベルに至るまで、JavaScriptを上から下に構築できます。これにより、開発(雇用を含む)が大幅に容易になります。
  • クローラーは、完全にレンダリングされたHTML応答を受け取ります。これは、たとえば、単一ページアプリケーションやNode.js上で実行されるwebsocketsアプリよりもはるかにSEOフレンドリーです。

短所:

  • CPUを集中的に使用する計算はNode.jsの応答性をブロックするため、スレッドプラットフォームの方が優れたアプローチです。または、計算をスケールアウトすることもできます(*)。
  • リレーショナルデータベースでNode.jsを使用するのは、まだかなり苦痛です(詳細については、以下を参照してください)。リレーショナル操作を実行しようとしている場合は、Rails、Django、ASP.Net MVCなどの他の環境を選択してください。

(*)CPUを集中的に使用する計算に代わる方法は、クライアントリクエストを非同期に処理するためのフロントエンドの「店員」としてNodeを維持するためのバックエンド処理を備えた高度にスケーラブルなMQバック環境を作成することです。

Node.jsを使用すべきではない場所

リレーショナルデータベースの背後にあるサーバー側のWebアプリケーション

例えば、Node.jsとExpress.jsをRuby on Railsに対して比較すると、リレーショナルデータアクセスに関しては、後者を支持する明確な決定があります。

Node.jsのリレーショナルDBツールは、競合製品と比較してまだ未開発です。一方、Railsは、DBスキーマ移行サポートツールおよびその他のGems(意図された)と共に、すぐにデータアクセスのセットアップを自動的に提供します。 Railsとそのピアフレームワークには、成熟した実績のあるActive RecordまたはData Mapperデータアクセスレイヤーの実装があり、それらを純粋なJavaScriptで複製しようとしても見逃しがちです。(*)

それでも、JSを最後までやりたい場合は、SequelizeとNode ORM2をご覧ください。

(*)RailsバックエンドとリレーショナルDBへの簡単なアクセスを維持しながら、Node.jsを一般公開のファサードとしてのみ使用することは可能です。

重いサーバー側の計算/処理

重い計算に関しては、Node.jsは最適なプラットフォームではありません。いいえ、間違いなくNode.jsでフィボナッチ計算サーバーを構築する必要はありません。一般に、CPU集中型の操作は、ノードがイベント駆動型の非ブロッキングI / Oモデルで提供するスループットの利点をすべて無効にします。これは、スレッドが数値計算で占有されている間、着信要求がブロックされるためです。

前述のように、Node.jsはシングルスレッドであり、単一のCPUコアのみを使用します。マルチコアサーバーでの同時実行の追加に関しては、ノードコアチームによってクラスターモジュールの形でいくつかの作業が行われています。また、nginxを介してリバースプロキシの背後で複数のNode.jsサーバーインスタンスを簡単に実行することもできます。

クラスタリングでは、より適切な環境で記述されたバックグラウンドプロセスに重い計算をすべてオフロードし、RabbitMQなどのメッセージキューサーバーを介して通信させる必要があります。

バックグラウンド処理が最初に同じサーバーで実行される場合でも、そのようなアプローチは非常に高いスケーラビリティを実現する可能性があります。これらのバックグラウンド処理サービスは、前面のWebサーバーの負荷を構成する必要なく、個別のワーカーサーバーに簡単に分散できます。

もちろん、他のプラットフォームでも同じアプローチを使用しますが、Node.jsを使用すると、各リクエストは非常に迅速かつ効率的に処理される小さなタスクであるため、これまでに説明した高いreqs / secスループットが得られます。

結論

Node.jsを理論から実践まで、目標と野心から始まり、スイートスポットと落とし穴で終わりました。 Nodeで問題が発生すると、ほとんどの場合、ブロック操作がすべての悪の根源であるという事実に帰着します。Nodeの誤用の99%は直接的な結果として発生します。

要確認:Node.jsは、計算スケーリングの問題を解決するために作成されたものではありません。 I / Oスケーリングの問題を解決するために作成されたもので、非常にうまく機能します。

ユースケースにCPU集中型の操作が含まれておらず、ブロッキングリソースにアクセスしていない場合、Node.jsの利点を活用して、高速でスケーラブルなネットワークアプリケーションを楽しむことができます。リアルタイムWebへようこそ。