この記事では、Nodejs の非同期 I/O について予備的に理解します。一定の参考値があるので、困っている友達が参考になれば幸いです。
# 「非同期」という用語は、実際には Node.js より前に生まれました。しかし、ほとんどの高級プログラミング言語では、非同期が発生することはまれです。多くの高級言語やオペレーティング プラットフォームの中で、Node は主要なプログラミング手法と設計コンセプトとして非同期を初めて使用しました。 [関連する推奨事項: "nodejs チュートリアル"]
非同期 I/O、イベント駆動型およびシングルスレッドが Node の基調を構成し、イベント駆動型および非同期 I/O 設計が構成されます。 Nginx と Node の概念は比較的似ています。 Nginx は純粋な C で書かれており、優れたパフォーマンスを備え、クライアントの接続を管理する強力な機能を備えていますが、依然としてさまざまな同期プログラミング言語による制限があります。しかし、Node は万能であり、クライアントからの大量の同時リクエストを処理するサーバーとして使用したり、ネットワーク内のさまざまなアプリケーションに同時リクエストを行うクライアントとしても使用できます。
なぜ非同期 I/O が Node で非常に重要なのか?これは、Node がネットワーク用に設計されているためです。 , 同時実行性は、現代のプログラミングの標準機能になっています。
「高パフォーマンス JavaScript」では、スクリプトの実行時間が 100 ミリ秒を超えると、ユーザーは、ページが停止しています。ページが応答を停止したと考えてください。 B/S モデルでは、ネットワーク速度の制限により、Web ページのリアルタイム エクスペリエンスに大きな問題が発生します。
Web ページが一時的にリソースを取得して同期的に取得する必要がある場合、JavaScript は実行を続行する前に、サーバーからリソースが完全に取得されるまで待つ必要があります。この間、UI は一時停止し、ユーザーのインタラクションに応答しません。このようなユーザー エクスペリエンスは非常に悪くなります。非同期リクエストでは、リソースのダウンロード中に JavaScript と UI の実行が待機状態にならず、ユーザーの操作に応答し続けることができます。
同様に、フロントエンドは非同期使用によって UI のブロックを排除できますが、フロントエンドがリソースを取得する速度はバックエンドの応答速度にも依存します。リソースが 2 つの異なる場所から返されたデータから得られる場合、最初のリソースは M ミリ秒を消費し、2 番目のリソースは N ミリ秒を消費します。同期方式を使用する場合、2 つのリソースを取得するのにかかる時間は M N ミリ秒です。非同期方式では、最初のリソースの取得は 2 番目のリソースの取得をブロックせず、消費される時間は max(M,N) です。
Web サイトまたはアプリケーションが拡大し続けるにつれて、M と N の値は直線的に増加し、非同期のパフォーマンスが同期よりも優れるようになります。
ビジネス シナリオで完了する必要がある一連の無関係なタスクがあると仮定すると、次の 2 つの主流の方法があります。
ポーリングと呼ばれます。
既存のポーリング テクノロジには、主に read、select、poll、epoll、kqueue が含まれます。ここでは、epoll のポーリング原理についてのみ説明します。epoll は、Linux で最も効率的な I/O イベント通知メカニズムです。ポーリングを開始するときに、I/O イベントが検出されない場合、イベントが発生して起動するまでスリープ状態になります。クエリを走査する代わりにイベント通知と実行コールバック メソッドを実際に使用するため、CPU を無駄にせず、実行効率が高くなります。
ポーリング テクノロジは、完全なデータを確実に取得するためのノンブロッキング I/O のニーズを満たしますが、プログラムにとっては依然として一種の同期とみなされます。アプリケーションはまだ I/O が完全に戻るまで待機する必要がありますが、待機にはまだ時間がかかります。待機中、CPU はファイル記述子を反復処理するか、時間が発生するまでスリープするために使用されます。
一部のスレッドにブロッキング I/O またはノンブロッキング I/O とポーリング テクノロジを実行させ、スレッドが実行できるようにすることでデータ取得を完了します。計算を実行し、スレッド間の通信を通じて I/O から取得したデータを転送します。これにより、非同期 I/O が簡単に実装されます (ただし、これはシミュレートされています)
しかし、当初、ノードは *nix にありました。プラットフォームは libeio と libev を使用して実装しますI/O 部分を実装し、非同期 I/O を実装します。 Node v0.9.3 では、非同期 I/O を完了するためにスレッド プールが実装されています。
Windows の IOCP は、非同期メソッドの呼び出し、I/O 完了後の通知の待機、コールバックの実行など、Li Xiang の非同期 I/O をある程度提供します。ユーザーはポーリングを考慮する必要はありません。ただし、その内部は依然としてスレッド プールの原則に基づいており、異なるのは、これらのスレッド プールがシステム カーネルによって管理されることです。
Windows プラットフォームと *nix プラットフォームの違いにより、Node は抽象カプセル化層として libuv を提供するため、すべてのプラットフォーム互換性の判断はこの層によって完了し、上位ノードと下位ノードのカスタマイズが保証されます。スレッド プールと IOCP は完全に独立しています。
Node はシングルスレッドであるとよく言いますが、ここでのシングルスレッドとは、単一のスレッドで実行される JavaScript のことです。 Node には、*nix であっても Windows プラットフォームであっても、内部で I/O タスクを完了するスレッド プールがあります。
イベント ループ、オブザーバー、およびリクエスト オブジェクトにより、非同期 I/O リンク全体が完成します。
イベント ループは Node 独自の実行モデルであり、コールバック関数が非常に一般的になります。
プロセスが開始されると、Node は while(true) に似たループを作成します。ループ本体が実行されるたびに、それを Tick と呼びます。各 Tick のプロセスは、処理対象のイベントがあるかどうかを確認し、存在する場合はイベントとそれに関連するコールバック関数を取得します。関連するコールバック関数が存在する場合は、それらを実行します。次に、次のループに入り、処理するイベントがなくなった場合はプロセスを終了します。
各イベント ループには 1 つ以上のオブザーバーがあり、監視するイベントがあるかどうかを判断するために使用されます。処理されるプロセスでは、これらのオブザーバーに処理するイベントがあるかどうかを尋ねます。
Node では、イベントは主にネットワーク リクエスト、ファイル I/O などから発生します。これらの時間に対応するオブザーバーには、ファイル I/O オブザーバー、ネットワーク I/O オブザーバーなどが含まれます。オブザーバーがイベントを分類しました。
イベント ループは、典型的なプロデューサー/コンシューマー モデルです。非同期 I/O やネットワーク リクエストなどがイベントのプロデューサーであり、常にさまざまな種類のイベントを Node に提供し、これらのイベントは対応するオブザーバーに配信され、イベント ループはオブザーバーからイベントを取り出して処理します。
ノードの非同期 I/O 呼び出しの場合、コールバック関数は開発者によって呼び出されません。呼び出しを開始する JavaScript から I/O 操作を実行するカーネルへの移行プロセスでは、Request オブジェクト
という製品があり、以下では fs.open() メソッドが小さな例。
fs.open = function(path,flags,mode,callback){ //... binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, callback); }
fs.open() の機能は、指定されたパスとパラメータに基づいてファイルを開き、ファイル記述子を取得することです。これは、後続のすべての I/O 操作に対する最初の試行操作です。 JavaScript レベルのコードは、C コア モジュールを呼び出すことによって下位レベルの操作を実行します。
は、Node のコア モジュールを呼び出す JavaScript に取り組んでいます。コア モジュールは C モジュールを呼び出し、組み込みモジュールは libuv を通じてシステム コールを行います。これは古典的なものです。 Node でメソッドを呼び出します。ここで、libuv はカプセル化層として機能し、基本的に uv_fs_open() メソッドを呼び出す 2 つのプラットフォーム実装があります。 uv_fs_open() の呼び出しプロセス中に、JavaScript レイヤーから渡されたパラメーターと現在のメソッドがリクエスト オブジェクトにカプセル化され、コールバック関数がこのオブジェクトのプロパティに設定されます。オブジェクトがパッケージ化された後、オブジェクトはスレッド プールにプッシュされて実行を待ちます。
この時点で、JavaScript 呼び出しはすぐに戻り、JavaScript レベルによって開始された非同期呼び出しの最初のフェーズが終了します。 JavaScript スレッドは、現在のタスクに対して後続の操作を実行し続けることができます。
リクエスト オブジェクトは、非同期 I/O プロセスの重要な中間生成物です。I/O 操作の完了後の実行とコールバック処理を待機するためにスレッド プールに送信される状態を含む、すべての状態がこのオブジェクトに保存されます。
リクエスト オブジェクトをアセンブルし、I/O スレッド プールに送信して実行を待機します。これは I/O の最初の部分のみを完了しますコールバック通知は最初の部分であり、その 2 です。
スレッド プール内の I/O 操作が呼び出された後、取得された結果は req->result 属性に格納され、PostQueueCompletionStatus() が呼び出されて、現在のオブジェクト操作が IOCP に通知されます。完成しました。
この時点で、非同期 I/O プロセス全体が完全に終了しました。
イベント ループ、オブザーバー、リクエスト オブジェクト、および I/O スレッド プールは、一緒になってノードの非同期 I/O モデルの基本要素を構成します。
整理すると、非同期 I/O のキーワードとして、シングル スレッド、イベント ループ、オブザーバー、I/O スレッド プールがいくつか抽出できます。シングルスレッドとスレッドプールは少し矛盾しているように思えます。 JavaScript はシングルスレッドであるため、マルチコア CPU を最大限に活用できないことは容易に理解できます。実際、Node では、JavaScript がシングルスレッドであることを除いて、Node 自体は実際にはマルチスレッドですが、I/O スレッドが使用する CPU は少なくなります。また、ユーザーコードは並列実行できないことを除き、すべての I/O (ディスク I/O、ネットワーク I/O など) は並列実行できます。
プログラミング関連の知識について詳しくは、プログラミング ビデオをご覧ください。 !
以上がNodejs の非同期 I/O についての予備的な理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。