私はNodeプログラマーではありませんが、シングルスレッドのノンブロッキングIOモデルがどのように機能するかに興味があります。この記事を読んだ後、Node.js イベント ループを理解する、私はそれについて本当に混乱しています。モデルの例が示されています:
c.query(
'SELECT SLEEP(20);',
function (err, results, fields) {
if (err) {
throw err;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('<html><head><title>Hello</title></head><body><h1>Return from async DB query</h1></body></html>');
c.end();
}
);
質問:リクエスト A (最初に来る) と B の 2 つのリクエストがある場合、スレッドは 1 つしかないため、サーバー側プログラムはリクエスト A を最初に処理します。SQL クエリを実行すると、I/O 待機を表すスリープ ステートメントが実行されます。プログラムは待機状態で停止しI/O
、背後にある Web ページをレンダリングするコードを実行できません。プログラムは待機中にリクエスト B に切り替わりますか? 私の意見では、シングル スレッド モデルのため、あるリクエストを別のリクエストから切り替える方法はありません。ただし、サンプル コードのタイトルには、コード以外はすべて並列で実行されると書かれています。
(追記: Node を使ったことがないので、コードを誤解しているかどうかはわかりません。) 待機中に Node が A から B に切り替える方法を教えてください。また、 Node のシングルスレッド非ブロッキング IO モデルをわかりやすく説明していただけますか。ご協力いただければ幸いです。 :)
ベストアンサー1
Node.jsはリブヴは、サポートされている OS (少なくとも Unix、OS X、Windows) によって提供される非同期 (非ブロッキング) 入出力用の API/システム コールを抽象化するクロスプラットフォーム ライブラリです。
非同期IO
このプログラミング モデルでは、ファイル システムによって管理されるデバイスおよびリソース (ソケット、ファイル システムなど) に対するオープン/読み取り/書き込み操作は、呼び出しスレッドをブロックせず(一般的な同期 C のようなモデルのように)、新しいデータまたはイベントが利用可能になったときに通知されるようにプロセスをマークするだけです (カーネル/OS レベルのデータ構造内)。Web サーバーのようなアプリの場合、プロセスは通知されたイベントがどの要求/コンテキストに属しているかを判断し、そこから要求の処理を続行する責任があります。これは、単一スレッド プロセスが新しいイベントを処理するために、OS がプロセスのディスパッチャに譲歩する必要があったため、必然的に OS への要求の発信元とは異なるスタック フレーム上にいることを意味することに注意してください。
私が説明したモデルの問題は、本質的に非連続的であるため、プログラマーにとって馴染みがなく、理解しにくいことです。「関数 A でリクエストを行い、その結果を別の関数で処理する必要がありますが、通常、関数 A のローカルは利用できません。」
ノードのモデル(継続渡しスタイルとイベントループ)
Node は、JavaScript の言語機能を活用して、プログラマーに特定のプログラミング スタイルを採用させることで、このモデルをもう少し同期的に見せることで、この問題に取り組んでいます。IO を要求するすべての関数にはfunction (... parameters ..., callback)
、 のようなシグネチャがあり、要求された操作が完了したときに呼び出されるコールバックを指定する必要があります (ほとんどの時間は、OS が完了を通知するのを待つことに費やされます。この時間は他の作業に費やすことができます)。Javascript のクロージャ サポートにより、コールバックの本体内で、外部 (呼び出し側) 関数で定義した変数を使用できます。これにより、Node ランタイムによって個別に呼び出されるさまざまな関数間で状態を保持できます。継続渡しスタイル。
さらに、IO 操作を生成する関数を呼び出した後、呼び出し関数は通常、return
ノードのイベント ループを制御します。このループは、実行がスケジュールされた次のコールバックまたは関数を呼び出します (おそらく、対応するイベントが OS によって通知されたため)。これにより、複数の要求を同時に処理できます。
ノードのイベント ループはカーネルのディスパッチャに似ていると考えることができます。カーネルは保留中の IO が完了するとブロックされたスレッドの実行をスケジュールしますが、ノードは対応するイベントが発生したときにコールバックをスケジュールします。
高度な同時実行性、並列処理なし
最後に、「あなたのコード以外はすべて並列に実行される」というフレーズは、Node.jsがあなたのコードが単一のスレッドで数十万のオープンソケットからのリクエストを同時に処理できるようにするという点をうまく捉えています。これは、すべてのjsロジックを単一の実行ストリームで多重化してシーケンス化することによって実現されます(ただし、「すべてが並列に実行される」と言うのはおそらくここでは正しくありません。同時実行性と並列性 - 違いは何ですか?)。ほとんどの時間は実際にはネットワークまたはディスク (データベース/ソケット) の待機に費やされ、ロジックは実際には CPU を集中的に使用しないため、これは Web アプリケーション サーバーに非常に適しています。つまり、これは IO バウンドのワークロードに適しています。