144

nodejsアーキテクチャには内部的に2つのイベントループがありますか?

  • libev/libuv
  • v8 JavaScript イベント ループ

I/O 要求で、ノードは libeio への要求をキューに入れ、li​​bev を使用してイベントを介してデータの可用性を通知し、最終的にそれらのイベントはコールバックを使用して v8 イベントループによって処理されますか?

基本的に、libev と libeio は nodejs アーキテクチャにどのように統合されていますか?

nodejs の内部アーキテクチャを明確に示すために利用できるドキュメントはありますか?

4

8 に答える 8

177

私は個人的にnode.js&v8のソースコードを読んでいます。

ネイティブ モジュールを作成するために node.js アーキテクチャを理解しようとしたときに、あなたと同じような問題に遭遇しました。

私がここに投稿しているのは、node.js に関する私の理解であり、これも少し軌道に乗っていない可能性があります。

  1. Libevは、単純なイベント ループ操作を実行するために実際に node.js の内部で実行されるイベント ループです。これはもともと *nix システム用に書かれています。Libev は、プロセスを実行するためのシンプルかつ最適化されたイベント ループを提供します。libev の詳細については、こちらを参照してください。

  2. LibEioは非同期で入出力を行うためのライブラリです。ファイル記述子、データ ハンドラー、ソケットなどを処理します。詳細については、こちらをご覧ください

  3. LibUvは、libeio、libev、c-ares (DNS 用)、および iocp (Windows 非同期 io 用) の上にある抽象化レイヤーです。LibUv は、イベント プール内のすべての io およびイベントを実行、維持、および管理します。(libeio スレッドプールの場合)。libUv に関するRyan Dahl のチュートリアルを確認してください。これにより、libUv 自体がどのように機能するかについて理解が深まり、libuv と v8 の上で node.js がどのように機能するかが理解できるようになります。

JavaScript のイベント ループだけを理解するには、これらのビデオを見ることを検討してください。

libeio を node.js で使用して非同期モジュールを作成する方法を確認するには、この例を参照してください。

基本的に、node.js 内で起こることは、v8 ループが実行され、すべての JavaScript パーツと C++ モジュールを処理することです [メイン スレッドで実行されている場合 (公式ドキュメント node.js 自体はシングル スレッドであるため)]。メイン スレッドの外側にある場合、libev と libeio はスレッド プールでそれを処理し、libev はメイン ループとの対話を提供します。したがって、私の理解では、node.js には 1 つの永続的なイベント ループがあります。それが v8 イベント ループです。C++ 非同期タスクを処理するために、スレッドプールを使用しています [libeio & libev 経由]。

例えば:

eio_custom(Task,FLAG,AfterTask,Eio_REQUEST);

すべてのモジュールに表示されるのは、通常Task、スレッドプールで関数を呼び出しています。完了するとAfterTask、メイン スレッドで関数が呼び出されます。一方Eio_REQUEST、スレッドプールとメインスレッド間の通信を提供することを動機とする構造/オブジェクトにすることができるリクエストハンドラーです。

于 2012-06-18T12:02:27.273 に答える
21

議論されたエンティティの一部 (例: libev など) は、しばらく経ったために関連性を失ったように見えますが、この質問にはまだ大きな可能性があると思います。

今日のノードのコンテキストで、抽象的なUNIX環境での抽象的な例を使用して、イベント駆動モデルの動作を説明してみましょう。

プログラムの視点:

  • スクリプト エンジンがスクリプトの実行を開始します。
  • CPU バウンド操作が発生するたびに、インライン (実マシン) で完全に実行されます。
  • I/O バウンド操作が発生するたびに、リクエストとその完了ハンドラが「イベント機構」(仮想マシン) に登録されます。
  • スクリプトが終了するまで、上記と同じ操作を繰り返します。CPU バウンド操作 - インラインで I/O バウンドのものを実行し、上記のように機械に要求します。
  • I/O が完了すると、リスナーがコールバックされます。

上記のイベント機構は、libuv AKA イベント ループ フレームワークと呼ばれます。ノードはこのライブラリを活用して、イベント駆動型プログラミング モデルを実装します。

ノードの視点:

  • ランタイムをホストするスレッドを 1 つ用意します。
  • ユーザー スクリプトを取得します。
  • ネイティブにコンパイル [v8 を活用]
  • バイナリをロードし、エントリ ポイントにジャンプします。
  • コンパイルされたコードは、プログラミング プリミティブを使用して、CPU バウンド アクティビティをインラインで実行します。
  • 多くの I/O およびタイマー関連のコードには、ネイティブ ラップがあります。たとえば、ネットワーク I/O です。
  • したがって、I/O 呼び出しはスクリプトから C++ ブリッジにルーティングされ、I/O ハンドルと完了ハンドラが引数として渡されます。
  • ネイティブ コードは libuv ループを実行します。ループを取得し、I/O を表す低レベル イベントとネイティブ コールバック ラッパーを libuv ループ構造にエンキューします。
  • ネイティブ コードがスクリプトに戻ります。現時点では I/O は発生しません。
  • すべての非 I/O コードが実行され、すべての I/O コードが libuv に登録されるまで、上記の項目が何度も繰り返されます。
  • 最後に、システムに実行するものが残っていない場合、ノードは制御を libuv に渡します。
  • libuv が動作を開始し、登録されているすべてのイベントを取得し、オペレーティング システムにクエリを実行して操作性を取得します。
  • 非ブロッキング モードで I/O の準備ができているものは、ピックアップされ、I/O が実行され、それらのコールバックが発行されます。次から次へと。
  • まだ準備ができていないもの (たとえば、他のエンドポイントがまだ何も書き込んでいないソケット読み取り) は、利用可能になるまで OS でプローブされ続けます。
  • ループは、増加し続けるタイマーを内部的に維持します。アプリケーションが遅延コールバック (setTimeout など) を要求すると、この内部タイマー値を利用して、コールバックを開始する適切な時間を計算します。

ほとんどの機能はこの方法で提供されますが、ファイル操作の一部 (非同期バージョン) は、libuv に適切に統合された追加のスレッドの助けを借りて実行されます。ネットワーク I/O 操作は、他のエンドポイントがデータで応答するなどの外部イベントを期待して待機できますが、ファイル操作にはノード自体からの作業が必要です。たとえば、ファイルを開いて fd にデータの準備が整うのを待っても、実際には誰も読んでいないので、それは起こりません! 同時に、メイン スレッドでインラインでファイルから読み取ると、プログラム内の他のアクティビティがブロックされる可能性があり、目に見える問題が発生する可能性があります。これは、CPU バウンドのアクティビティに比べてファイル操作が非常に遅いためです。そのため、内部ワーカー スレッド (UV_THREADPOOL_SIZE 環境変数で構成可能) を使用してファイルを操作し、

お役に立てれば。

于 2016-04-26T08:34:01.363 に答える
18

libuvの紹介

node.jsプロジェクトは、ブラウザーから切り離された JavaScript 環境として 2009 年に始まりました。Google のV8と Marc Lehmann のlibevを使用して、node.js は I/O のモデル (イベント化された) をプログラミングのスタイルに適した言語と組み合わせました。ブラウザによって形作られた方法のためです。node.js の人気が高まるにつれ、Windows で動作させることが重要になりましたが、libev は Unix でしか動作しませんでした。kqueue や (e)poll などのカーネル イベント通知メカニズムに相当する Windows は IOCP です。libuv は、プラットフォームに応じて libev または IOCP を抽象化したもので、libev に基づく API をユーザーに提供します。node-v0.9.0 バージョンの libuv libev では削除されました。

また、@ BusyRichによるNode.js のイベント ループを説明する 1 つの図


更新 2017 年 5 月 9 日

このドキュメントNode.js event loop に従って

次の図は、イベント ループの操作順序の概要を簡単に示しています。

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

注: 各ボックスは、イベント ループの「フェーズ」と呼ばれます。

フェーズの概要

  • timers : このフェーズは、 および によってスケジュールされたコールバックを実行しsetTimeout()ますsetInterval()
  • I/O コールバック: ほとんどすべてのコールバックを実行しますが、クローズ コールバック、タイマーによってスケジュールされたコールバック、およびsetImmediate().
  • idle, prepare : 内部でのみ使用されます。
  • poll : 新しい I/O イベントを取得します。ノードは、適切な場合にここでブロックします。
  • check :setImmediate()ここでコールバックが呼び出されます。
  • コールバックを閉じますsocket.on('close', ...)

イベント ループの実行ごとに、Node.js は非同期 I/O またはタイマーを待機しているかどうかを確認し、待機していない場合は正常にシャットダウンします。

于 2016-01-02T13:15:04.857 に答える
0

簡単に言えば、Node イベント ループはアーキテクチャ レベルでのサイクルまたはループであり、Javascript コードが非同期コードを処理するのに役立ちます。

イベント ループの内部にはさまざまなループ/サイクルがあり、setTimeouts、setimmediate、ファイル システム、ネットワーク リクエスト、プロミスなどの適切なジョブを処理するために使用されます。

于 2021-06-23T08:48:45.167 に答える
0

このpbkdf2関数には JavaScript の実装がありますが、実際にはすべての作業を C++ 側に委譲します。

env->SetMethod(target, "pbkdf2", PBKDF2);
  env->SetMethod(target, "generateKeyPairRSA", GenerateKeyPairRSA);
  env->SetMethod(target, "generateKeyPairDSA", GenerateKeyPairDSA);
  env->SetMethod(target, "generateKeyPairEC", GenerateKeyPairEC);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_NAMED_CURVE);
  NODE_DEFINE_CONSTANT(target, OPENSSL_EC_EXPLICIT_CURVE);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS1);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingPKCS8);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSPKI);
  NODE_DEFINE_CONSTANT(target, kKeyEncodingSEC1);
  NODE_DEFINE_CONSTANT(target, kKeyFormatDER);
  NODE_DEFINE_CONSTANT(target, kKeyFormatPEM);
  NODE_DEFINE_CONSTANT(target, kKeyTypeSecret);
  NODE_DEFINE_CONSTANT(target, kKeyTypePublic);
  NODE_DEFINE_CONSTANT(target, kKeyTypePrivate);
  env->SetMethod(target, "randomBytes", RandomBytes);
  env->SetMethodNoSideEffect(target, "timingSafeEqual", TimingSafeEqual);
  env->SetMethodNoSideEffect(target, "getSSLCiphers", GetSSLCiphers);
  env->SetMethodNoSideEffect(target, "getCiphers", GetCiphers);
  env->SetMethodNoSideEffect(target, "getHashes", GetHashes);
  env->SetMethodNoSideEffect(target, "getCurves", GetCurves);
  env->SetMethod(target, "publicEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_encrypt_init,
                                         EVP_PKEY_encrypt>);
  env->SetMethod(target, "privateDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_decrypt_init,
                                         EVP_PKEY_decrypt>);
  env->SetMethod(target, "privateEncrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPrivate,
                                         EVP_PKEY_sign_init,
                                         EVP_PKEY_sign>);
  env->SetMethod(target, "publicDecrypt",
                 PublicKeyCipher::Cipher<PublicKeyCipher::kPublic,
                                         EVP_PKEY_verify_recover_init,
                                         EVP_PKEY_verify_recover>);

リソース: https://github.com/nodejs/node/blob/master/src/node_crypto.cc

Libuv モジュールには、標準ライブラリ内の非常に特定の関数に関連する別の責任があります。

一部の標準ライブラリ関数呼び出しでは、Node C++ 側と Libuv は、イベント ループの外側でコストのかかる計算を完全に実行することを決定します。

pbkdf2代わりに、スレッド プールと呼ばれるものを使用します。スレッド プールは、関数などの計算コストの高いタスクを実行するために使用できる一連の 4 つのスレッドです。

デフォルトでは、Libuv はこのスレッド プールに 4 つのスレッドを作成します。

イベント ループで使用されるスレッドに加えて、アプリケーション内で発生する必要がある高価な計算をオフロードするために使用できる 4 つのスレッドがあります。

Node 標準ライブラリに含まれる関数の多くは、このスレッド プールを自動的に利用します。pbkdf2機能もその一つです。

このスレッド プールの存在は非常に重要です。

そのため、Node は真のシングル スレッドではありません。Node が計算コストの高いタスクを実行するために使用する他のスレッドがあるからです。

計算コストの高いタスクを実行する責任がイベント プールにある場合、Node アプリケーションは他に何もできません。

私たちの CPU は、スレッド内のすべての命令を 1 つずつ実行します。

スレッド プールを使用することで、計算が行われている間にイベント ループ内で他のことを行うことができます。

于 2019-02-17T00:58:26.320 に答える