web-dev-qa-db-ja.com

Node.jsとCPU集中型のリクエスト

Node.js HTTPサーバーをいじくり始めたので、サーバーサイドのJavascriptを書くのが本当に好きですが、WebアプリケーションにNode.jsを使用し始めるのを妨げています。

私は非同期I/Oの概念全体を理解していますが、画像操作や大きなデータセットの並べ替えなど、手続き型コードがCPUに非常に集中するEdgeのケースについて多少心配しています。

私が理解しているように、サーバーは、ユーザーのリストの表示やブログ投稿の表示などの単純なWebページ要求に対して非常に高速です。ただし、グラフィックスを生成したり、数千の画像のサイズを変更したりする非常にCPU集中型のコード(たとえば、管理バックエンド)を作成する場合、要求は非常に遅くなります(数秒)。このコードは非同期ではないため、これらの数秒間にサーバーに送信されるすべてのリクエストは、遅いリクエストが完了するまでブロックされます。

1つの提案は、CPUを集中的に使用するタスクにWebワーカーを使用することでした。ただし、Webワーカーは、独立したJSファイルを含めることで機能するため、クリーンなコードを書くのが難しくなります。 CPUを集中的に使用するコードがオブジェクトのメソッドにある場合はどうなりますか? CPUを集中的に使用するメソッドごとにJSファイルを作成するのは、ややこしいことです。

別の提案は、子プロセスを生成することでしたが、それによりコードの保守性がさらに低下します。

この(知覚される)障害を克服するための提案はありますか? CPU負荷の高いタスクが非同期で実行されていることを確認しながら、Node.jsでクリーンなオブジェクト指向コードをどのように記述しますか?

202
Olivier Lalonde

必要なのはタスクキューです!長時間実行されるタスクをWebサーバーから移動するのは良いことです。各タスクを「別個の」jsファイルに保持すると、モジュール性とコードの再利用が促進されます。長期的にデバッグと保守を容易にする方法でプログラムを構成する方法について考えるように強制します。タスクキューのもう1つの利点は、ワーカーが別の言語で記述できることです。タスクをポップし、作業を行い、応答を書き戻すだけです。

このようなもの https://github.com/resque/resque

Githubがなぜそれを構築したかについての記事があります http://github.com/blog/542-introducing-resque

51
Tim

これはWebサーバーの定義の誤解です。クライアントとの「会話」にのみ使用する必要があります。高負荷タスクは、スタンドアロンプ​​ログラムに委任する必要があります(もちろん、JSで作成することもできます)。
おそらく汚れていると言うでしょうが、画像のサイズを変更することで立ち往生しているWebサーバープロセスは、さらに悪いことを保証します(Apacheが他のクエリをブロックしない場合でも)。それでも、コードの冗長性を回避するために、共通ライブラリを使用できます。

編集:私は類推を思いついた。 Webアプリケーションはレストランとして使用する必要があります。ウェイター(Webサーバー)と料理人(ワーカー)がいます。ウェイターはクライアントと連絡を取り、メニューの提供やベジタリアン料理の説明などの簡単なタスクを実行します。一方、彼らはより難しいタスクをキッチンに委任します。ウェイターは簡単なことしかしていないため、迅速に対応し、料理人は仕事に集中できます。

ここでのNode.jsは、一度に多くのリクエストを処理できる単一の非常に才能のあるウェイターであり、Apacheは、各リクエストを1つだけ処理する愚かなウェイターのギャングです。このNode.jsウェイターが料理を始めたら、それはすぐに大惨事になります。それでも、料理は、キッチンの混乱と応答性の漸進的な低下に言及せずに、大量のApacheウェイターでさえ使い果たす可能性があります。

280
mbq

CPUを集中的に使用するコードで非同期を実行するのではなく、並行して実行するようにします。 HTTPリクエストを処理しているスレッドから処理作業を取得する必要があります。この問題を解決する唯一の方法です。 NodeJSでの答えは cluster module で、子プロセスを生成して面倒な作業を行います。 (AFAIK Nodeにはスレッド/共有メモリの概念はありません。プロセスまたは無です)。アプリケーションの構成方法には2つのオプションがあります。 80個のHTTPサーバーを生成し、子プロセスで計算負荷の高いタスクを同期的に処理することにより、80/20ソリューションを取得できます。それを行うのは非常に簡単です。そのリンクでそれについて読むのに1時間かかるかもしれません。実際、そのリンクの上部にあるサンプルコードをはぎ取るだけで、そこにいる道の95%が得られます。

これを構成するもう1つの方法は、ジョブキューを設定し、キューを介して大きな計算タスクを送信することです。ジョブキューのIPCに関連付けられているオーバーヘッドが多いことに注意してください。これは、タスクがオーバーヘッドよりもかなり大きい場合にのみ役立ちます。

これらの他の答えがどれもmentionクラスタでさえないことに驚いています。

背景:非同期コードは、何かが発生するまで中断するコードであり、他のどこかで、コードが起動して実行を継続します。他のどこかで遅い何かが発生しなければならない非常に一般的なケースの1つはI/Oです。

非同期コードは、作業を行うのがプロセッサである場合は役に立ちません。それはまさに「計算集中型」タスクの場合です。

今、非同期コードはニッチに見えるかもしれませんが、実際には非常に一般的です。計算集中型のタスクには役に立たないことがあります。

I/Oの待機は、たとえばWebサーバーで常に発生するパターンです。サーバーに接続するすべてのクライアントはソケットを取得します。ほとんどの場合、ソケットは空です。ソケットがデータを受信するまで何もしたくないので、その時点でリクエストを処理します。内部では、NodeなどのHTTPサーバーがイベントライブラリ(libev)を使用して、何千ものオープンソケットを追跡しています。 OSはlibevに通知し、次にソケットの1つがデータを取得するとlibevがNodeJSに通知し、NodeJSがイベントキューにイベントを配置します。この時点でhttpコードが作動し、イベントを次々に処理します。ソケットにデータがあるまでイベントはキューに入れられないので、イベントはデータを待機することはありません。既にイベントが存在します。

シングルスレッドのイベントベースのWebサーバーは、ボトルネックがほとんど空のソケット接続の束で待機しており、アイドル接続ごとにスレッドまたはプロセス全体が必要でなく、250kをポーリングしたくない場合のパラダイムとして理にかなっていますデータがある次のソケットを見つけるためのソケット。

13
masonk

使用できるいくつかのアプローチ。

@Timが指摘しているように、メインサービングロジックの外部または並列に配置される非同期タスクを作成できます。正確な要件に依存しますが、 cron でもキューイングメカニズムとして機能できます。

WebWorkersは非同期プロセスで機能しますが、現在node.jsではサポートされていません。サポートを提供するいくつかの拡張機能があります。たとえば、 http://github.com/cramforce/node-worker

標準の「requires」メカニズムを使用して、モジュールとコードを再利用できます。ワーカーへの最初のディスパッチが、結果の処理に必要なすべての情報を渡すことを確認する必要があります。

7
Toby Hede

child_processを使用することが1つのソリューションです。ただし、生成される各子プロセスは、Go goroutinesと比較して多くのメモリを消費する可能性があります

kue などのキューベースのソリューションを使用することもできます

0
neo