なぜ地獄はNode.jsを使用するのでしょうか? ケースバイケースのチュートリアル

公開: 2022-03-11

序章

JavaScriptの人気の高まりは、JavaScriptに多くの変化をもたらし、今日のWeb開発の様相は劇的に異なります。 今日のWebでJavaScriptをサーバー上で実行したり、ブラウザーで実行したりできることは、ほんの数年前には想像しがたいことでした。あるいは、FlashやJavaアプレットなどのサンドボックス環境にカプセル化されていました。

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

Wikipediaが述べているように、「Node.jsは、GoogleのV8 JavaScriptエンジン、libuvプラットフォーム抽象化レイヤー、およびコアライブラリのパッケージ化されたコンパイルであり、それ自体は主にJavaScriptで記述されています。」 さらに、Node.jsの作成者であるRyan Dahlが、「Gmailなどのアプリケーションに触発された」プッシュ機能を備えたリアルタイムWebサイトの作成を目指していたことは注目に値します。 Node.jsで、彼は開発者に非ブロッキングのイベント駆動型I/Oパラダイムで作業するためのツールを提供しました。

ステートレス要求/応答パラダイムに基づくステートレスWebの20年以上の後に、私たちはついにリアルタイムの双方向接続を備えたWebアプリケーションを手に入れました。

一言で言えば、Node.jsは、WebSocket上でプッシュテクノロジーを採用しているリアルタイムWebアプリケーションで輝いています。 それについて何がそんなに革命的ですか? ステートレス要求/応答パラダイムに基づくステートレスWebの20年以上の後に、クライアントとサーバーの両方が通信を開始してデータを自由に交換できる、リアルタイムの双方向接続を備えたWebアプリケーションがついに完成しました。 。 これは、クライアントが常に通信を開始する典型的なWeb応答パラダイムとはまったく対照的です。 さらに、それはすべて、標準ポート80で実行されているオープンWebスタック(HTML、CSS、およびJS)に基づいています。

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

Node.jsは、そのすべての利点を備えており、独自の利点に依存している多くの著名な企業のテクノロジースタックで重要な役割を果たしています。 Node.js Foundationは、Node.jsFoundationのケーススタディページにある短いプレゼンテーションで企業がNode.jsを検討する必要がある理由に関するすべての最良の考え方を統合しました。

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

それはどのように機能しますか?

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

それは一口です。

それが本当に意味するのは、Node.jsは、Web開発の世界を支配する銀の弾丸の新しいプラットフォームではないということです。 代わりに、それは特定のニーズを満たすプラットフォームです。
つぶやき

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

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

従来のサーバースレッドとNode.jsサーバースレッドの図

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

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

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

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

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

パッケージ化されたモジュールの完全なリストは、npm Webサイトにあります。または、Node.jsとともに自動的にインストールされるnpmCLIツールを使用してアクセスできます。 モジュールエコシステムはすべての人に開かれており、npmリポジトリにリストされる独自のモジュールを誰でも公開できます。

今日最も有用なnpmモジュールのいくつかは次のとおりです。

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

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

Node.jsを使用する場所の例

チャット

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

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

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

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

サーバー側には、2つのことを実装する単純なExpress.jsアプリケーションがあります。

  1. メッセージボードと新しいメッセージ入力を初期化するための「送信」ボタンの両方を含むWebページを提供するGET /リクエストハンドラー、および
  2. WebSocketクライアントによって発行された新しいメッセージをリッスンするWebSocketサーバー。

クライアント側には、いくつかのハンドラーが設定されたHTMLページがあります。1つは入力メッセージを取得してWebSocketに送信する「送信」ボタンクリックイベント用で、もう1つは新しい着信メッセージをリッスンします。 WebSocketクライアント(つまり、サーバーがクライアントに表示するようになっている他のユーザーから送信されたメッセージ)。

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

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

Node.jsアプリケーションのクライアントとサーバーのWebSocketの図

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

オブジェクトDBの上にあるAPI

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

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

キューに入れられた入力

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

このアプローチでは、システムは高負荷の下でも応答性を維持します。これは、クライアントがデータ書き込みの成功をしっかりと確認する必要がない場合に特に役立ちます。 典型的な例は次のとおりです。バッチで処理され、後で使用されるまで使用されない、ユーザー追跡データのロギングまたは書き込み。 結果整合性(NoSQLの世界でよく使用される)が許容される場合(Facebookの「いいね」カウントの更新など)、即座に反映する必要のない操作も同様です。

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

メッセージキューを使用したNode.jsでのデータベースバッチ書き込みの図

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

データストリーミング

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

プロキシー

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

例:サーバー側のアプリケーションがサードパーティのリソースと通信したり、さまざまなソースからデータを取得したり、画像や動画などのアセットをサードパーティのクラウドサービスに保存したりする場合を考えてみます。

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

ブローカー-株取引業者のダッシュボード

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

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

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

Node-with-web-socketsが完全に適合するもう1つの一般的な使用例は、Webサイトの訪問者を追跡し、それらの相互作用をリアルタイムで視覚化することです。

ユーザーからリアルタイムの統計を収集したり、訪問者が目標到達プロセスの特定のポイントに到達したときに通信チャネルを開いて訪問者とのターゲットを絞ったインタラクションを導入することで、それを次のレベルに移動したりすることもできます。 (興味があれば、このアイデアはすでにCANDDiによって製品化されています。)

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

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

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

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

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

Node.jsを使用できる場所

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

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

長所:

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

短所:

  • CPUを集中的に使用する計算は、Node.jsの応答性をブロックするため、スレッド化されたプラットフォームの方が適しています。 または、計算をスケールアウトしてみることもできます[*]。
  • リレーショナルデータベースでNode.jsを使用することは、依然として非常に困難です(詳細については以下を参照してください)。 関係演算を実行しようとしている場合は、Rails、Django、ASP.NetMVCなどの他の環境を利用してください。
[*]これらのCPUを集中的に使用する計算の代わりに、バックエンド処理を備えた高度にスケーラブルなMQバックアップ環境を作成して、ノードを前面の「店員」としてクライアント要求を非同期的に処理することもできます。

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

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

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

Node.js用のリレーショナルDBツールはまだ初期段階にありました。 一方、Railsは、DBスキーマ移行サポートツールやその他のGems(しゃれを意図したもの)とともに、箱から出してすぐにデータアクセスのセットアップを自動的に提供します。 Railsとそのピアフレームワークには、成熟した実績のあるActiveRecordまたはDataMapperデータアクセスレイヤーの実装があります。[*]

しかし、状況は変わりました。 Sequelize、TypeORM、Bookshelfは、成熟したORMソリューションになるために長い道のりを歩んできました。 GraphQLクエリからSQLを生成する場合は、JoinMonsterをチェックする価値があるかもしれません。

[*] RailsのバックエンドとリレーショナルDBへの簡単なアクセスを維持しながら、Nodeをフロントエンドとしてのみ使用することは可能であり、珍しいことではありません。
関連:バックエンド:静的サイトの更新にGatsby.jsとNode.jsを使用する

ヘビーサーバーサイドの計算/処理

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

前述のように、Node.jsはシングルスレッドであり、単一のCPUコアのみを使用します。 マルチコアサーバーに同時実行性を追加する場合、Nodeコアチームによってクラスターモジュールの形式でいくつかの作業が行われています[参照:http://nodejs.org/api/cluster.html]。 nginxを介して、リバースプロキシの背後で複数のNode.jsサーバーインスタンスを非常に簡単に実行することもできます。

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

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

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

結論

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

Nodeでは、ブロック操作がすべての悪の根源です。Nodeの誤用の99%は、直接的な結果として発生します。
つぶやき

注意:Node.jsは、コンピューティングスケーリングの問題を解決するために作成されたものではありません。 これは、I/Oスケーリングの問題を解決するために作成されました。

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