数日前、友人と 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. 間隔を設定する2.1 説明2.2 パラメータ2.3 戻り値2.4 使用法3...
序文負荷分散には nginx を使用します。アーキテクチャのフロントエンドまたは中間層として、トラフ...
序文この記事では、mysqld プロセスをシャットダウンするプロセスと、MySQL インスタンスを安...
目次序文1. エラーログ2. バイナリログ1. バイナリログを有効にする2. バイナリログ形式3. ...
docker create コマンドは、イメージに基づいてコンテナを作成できます。このコマンドの効果...
1. asキーワードはアサーションを示すTypescript では、アサーションを表現する方法が 2...
1. Windows 10 Enterprise Editionに付属する仮想マシンHyper-Vを...
1. 遭遇したいくつかの問題2008 年にパフォーマンス テストを行っていたとき、パフォーマンス テ...
目次シンプルな CASEWHEN 関数:これは、CASEWHEN 条件式関数を使用するのと同じです。...
目次1. 開発環境から本番環境への移行2. 統一されたリクエストパスを設定する3. パッケージ化コマ...
序文最近、職場でこの要件に遭遇し、リモート接続を確立するのに 1 時間以上かかりました。ローカル コ...
読み取り専用入力を実現するには、無効と読み取り専用の 2 つの方法があります。当然、どちらの結果も読...
目次1. ビューフック1. ngAfterViewInit および ngAfterViewCheck...
目次1. はじめに2. 設定手順1. はじめに1. NAT モード (VMnet8) は、仮想マシン...
この記事では、オブジェクト パラメータをインスタンス化して MySQL でデータをクエリする方法を紹...