数日前、友人と Node.js の epoll とリクエスト処理に関する知識を交換しました。今日は、Node.js のリクエスト処理のロジックについて簡単に説明します。まず listen 関数から始めます。 int uv_tcp_listen(uv_tcp_t* tcp, int バックログ, uv_connection_cb cb) { // リクエストを処理するための戦略を設定します。以下の分析を参照してください。if (single_accept == -1) { const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); single_accept = (val != NULL && atoi(val) != 0); /* デフォルトではオフです。 */ } (単一受け入れ)の場合 tcp->フラグ |= UV_HANDLE_TCP_SINGLE_ACCEPT; // バインドを実行するかフラグを設定します。err = maybe_new_socket(tcp, AF_INET, flags); // リスニングを開始する if (listen(tcp->io_watcher.fd, backlog)) UV__ERR(errno) を返します。 // コールバックを設定します tcp->connection_cb = cb; tcp->フラグ |= UV_HANDLE_BOUND; // epoll が接続を監視するときに実行される io ウォッチャーのコールバックを設定します。tcp->io_watcher.cb = uv__server_io; // オブザーバー キューを挿入します。この時点では、epoll に追加されていません。poll io ステージは、処理のためにオブザーバー キューをトラバースします (epoll_ctl) uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN); 0を返します。 } createServer を実行すると、Libuv レイヤーが従来のネットワーク プログラミング ロジックに従うことがわかります。この時点で弊社のサービスが開始されます。ポーリング IO フェーズでは、リスニング ファイル記述子とコンテキスト (関心のあるイベント、コールバックなど) が epoll に登録されます。通常、epoll ではブロックされます。では、この時点で TCP 接続が入ると何が起こるでしょうか? epoll は最初にイベントをトリガーした fd を走査し、次に fd コンテキスト、つまり uvserver_io でコールバックを実行します。 uvserver_io を見てみましょう。 void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { // ループ処理、uv__stream_fd(stream)はサーバーに対応するfdです uv__stream_fd(ストリーム) != -1 の場合{ // accept を介してクライアントと通信するための fd を取得します。この fd はサーバーの fd と異なることがわかります。err = uv__accept(uv__stream_fd(stream)); // uv__stream_fd(stream) に対応する fd は非ブロッキングです。このエラーを返すということは、受け入れ可能な接続がないことを意味します。直接 return if (err < 0) { エラー == UV_EAGAIN || エラー == UV__ERR(EWOULDBLOCK) の場合 戻る; } //記録する stream->accepted_fd = err; // コールバックを実行します stream->connection_cb(stream, 0); /* stream->accepted_fd は -1 です。これは、accepted_fd がコールバック connection_cb で消費されたことを意味します。 それ以外の場合は、まず epoll でサーバーの fd の読み取りイベントを登録解除し、消費を待ってから再度登録します。つまり、リクエストを処理しなくなります。*/ ストリーム->accepted_fd != -1 の場合 { uv__io_stop(ループ、&stream->io_watcher、POLLIN); 戻る; } /* わかりました。accepted_fd は消費されましたが、まだ新しい fd を受け入れますか? UV_HANDLE_TCP_SINGLE_ACCEPT が設定されている場合、一度に 1 つの接続のみが処理され、その後、他のプロセスが受け入れる機会を与えるためにしばらくスリープ状態になります (マルチプロセス アーキテクチャの場合)。マルチプロセスアーキテクチャでない場合は、これを再度設定します。 これにより、接続処理が遅延します*/ if (stream->type == UV_TCP && (ストリーム->フラグ & UV_HANDLE_TCP_SINGLE_ACCEPT)) { 構造体timespecタイムアウト = {0, 1}; ナノスリープ(&timeout, NULL); } } } uv__server_io から、Libuv がループ内で継続的に新しい fd を受け入れ、コールバックを実行することがわかります。通常、コールバックは fd を消費し、処理する接続がなくなるまでサイクルが続きます。次に、コールバックで fd がどのように消費されるか、そしてループの数が多いと時間がかかりすぎて、Libuv イベント ループがしばらくブロックされるかどうかに注目しましょう。 tcp のコールバックは c++ レイヤーの OnConnection です。 //接続テンプレート <typename WrapType, typename UVType> がある場合にトリガーされるコールバック void ConnectionWrap<WrapType, UVType>::OnConnection(uv_stream_t* ハンドル, int ステータス) { // Libuv 構造体 WrapType* に対応する C++ レイヤー オブジェクトを取得します。 wrap_data = static_cast<WrapType*>(handle->data); CHECK_EQ(&wrap_data->handle_, reinterpret_cast<UVType*>(handle)); 環境* env = wrap_data->env(); ハンドルスコープ handle_scope(env->isolate()); コンテキスト::スコープ context_scope(env->context()); //クライアントと通信するためのオブジェクト Local<Value> client_handle; (ステータス == 0)の場合{ // クライアントの JavaScript オブジェクトとハンドルをインスタンス化します。 // オブジェクトを使用して新しい js レイヤーを作成します Local<Object> client_obj; if (!WrapType::Instantiate(env, wrap_data, WrapType::SOCKET) .ToLocal(&client_obj)) 戻る; // クライアントの JavaScript オブジェクトをアンラップします。 ラップタイプ* ラップ; // js レイヤーで使用されるオブジェクト client_obj に対応する c++ レイヤー オブジェクトを wrap に格納します。ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj); // 対応するハンドルを取得する uv_stream_t* クライアント = reinterpret_cast<uv_stream_t*>(&wrap->handle_); // handleAccept によって受信された fd の 1 つを取得してクライアントに保存し、クライアントがクライアントと通信できるようにします if (uv_accept(handle, client)) 戻る; クライアントハンドル = クライアントオブジェクト; } それ以外 { client_handle = 未定義(env->isolate()); } // コールバック js、client_handle は js レイヤーで新しい TCP を実行することと同等です Local<Value> argv[] = { Integer::New(env->isolate(), status), client_handle }; wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv); } コードは複雑に見えますが、uv_accept だけに注目する必要があります。 uv_accept の最初のパラメータはサーバーに対応するハンドルであり、2 番目のパラメータはクライアントとの通信を表すオブジェクトです。 int uv_accept(uv_stream_t* サーバー、uv_stream_t* クライアント) { 整数エラー; スイッチ (クライアント->タイプ) { UV_NAMED_PIPEの場合: UV_TCPの場合: // fdをクライアントに設定 err = uv__stream_open(client, サーバー->accepted_fd、 UV_HANDLE_READABLE または UV_HANDLE_WRITABLE のいずれかです。 壊す; // ... } クライアント->フラグ |= UV_HANDLE_BOUND; // fd が消費されたことをマークする サーバー->accepted_fd = -1; エラーを返します。 } uv_accept には主に 2 つのロジックがあります。クライアントと通信するための fd をクライアントに設定し、それを使用済みとしてマークして、前述の while ループを実行し続けることです。上位層では、クライアントに関連するオブジェクトです。Libuv 層では構造体、C++ 層では C++ オブジェクト、JS 層では JS オブジェクトです。この 3 つは、層ごとにカプセル化され、関連付けられています。コアとなるのは、Libuv クライアント構造体の fd で、クライアントと通信するための基礎となるチケットです。最後に、js レイヤーがコールバックされ、net.js の onconnection が実行されます。 onconnection は、クライアントとの通信を表す Socket オブジェクトをカプセル化します。これは C++ レイヤーのオブジェクトを保持し、C++ レイヤー オブジェクトは Libuv 構造体を保持し、Libuv 構造体は fd を保持します。 constソケット = 新しいソケット({ ハンドル: clientHandle、 半開きを許可: self.半開きを許可、 作成時に一時停止: 接続時に自己一時停止、 読み取り可能: true、 書き込み可能: true }); これで、nodejs の tcp 接続処理のコア プロセスに関するこの記事は終了です。nodejs の tcp 接続処理に関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: mysql data_dirの変更によって発生するエラー問題を解決する
>>: Dockerコンテナを使用してプロキシ転送とデータバックアップを実装する方法
目次1. データベース設計2. フロントエンドページ3. 完全なデモフロントエンド開発では、カスケー...
データベースは、どのオブジェクトにどのフィールドが含まれているかを照会します。 *を選択 sysob...
実装方法は3つのステップに分かれています。テンプレートに 2 つのボタンを設定し、v-if と v-...
タブ: カテゴリ + 説明タグバー: カテゴリ => ユーザーに現在地と目的地を知らせる1. ...
目次React Fiberとは何ですか?なぜReact Fiberなのか? React Fiberは...
半透明の境界線結果: 実装コード: <div> 半透明の境界線が見えますか? </...
テスト環境は、JDBCドライバを使用してMariaDB 5.7でセットアップされています。 <...
今はモバイルインターネットが急速に発展している時代です。スマートフォンやタブレットはますます普及し、...
問題の背景: docker を使用してプロジェクトをデプロイする場合、プロジェクト内で印刷コントロー...
この記事は、Free Code Camp の基本アルゴリズム スクリプト「文字列内の最長の単語を見つ...
多くのウェブサイトでは、入力ボックスにヒントテキストが表示されています。入力ボックスにヒントテキスト...
NGINX の全体的なアーキテクチャは、連携して動作する一連のプロセスによって特徴付けられます。メイ...
<meta name="viewport" content="...
前のこれは古くからある古典的な質問です。以前読者から質問があったので、ここでお答えします。簡単な例か...
次のコードは、MySQL 5.7.22 バイナリ パッケージのインストール方法を紹介しています。具体...