Nodejs エラー処理プロセス記録

Nodejs エラー処理プロセス記録

この記事では、接続エラー ECONNREFUSED を例に、Node.js がエラーを処理するプロセスを確認します。 次のようなコードがあるとします

1. const net = require('net');  
2. net.connect({ポート: 9999})

このマシンでポート 9999 がリッスンしていない場合は、次の出力が表示されます。

1. イベント.js:170  
2. throw er; // 処理されない「エラー」イベント  
3. ^  
4.    
5. エラー: connect ECONNREFUSED 127.0.0.1:9999  
6. TCPConnectWrap.afterConnect [oncomplete として] (net.js:1088:14)  
7. 次の場合に「エラー」イベントが発行されます:  
8. でemitErrorNT (internal/streams/destroy.js:91:8)  
9. で、emitErrorAndCloseNT (internal/streams/destroy.js:59:3)  
10. processTicksAndRejections で (internal/process/task_queues.js:81:17)

connect の呼び出しプロセスを簡単に見てみましょう。

1. const req = new TCPConnectWrap();  
2. req.oncomplete = afterConnect;  
3. req.address = アドレス;  
4. req.port = ポート;  
5. req.localAddress = localAddress;  
6. req.localPort = localPort;  
7. // 接続を確立するために 3 ウェイ ハンドシェイクを開始します 8. err = self._handle.connect(req, address, port);

次に、C++レイヤーの接続ロジックを見てみましょう。

1. エラー = req_wrap->Dispatch(uv_tcp_connect,  
2. &wrap->handle_,  
3. reinterpret_cast<const sockaddr*>(&addr)、  
4. AfterConnect);

C++ レイヤーは Libuv の uv_tcp_connect を直接呼び出し、コールバックを AfterConnect に設定します。次に、libuv の実装を見てみましょう。

1. 行う {  
2. エラー番号 = 0;  
3. // 非ブロッキング呼び出し 4. r = connect(uv__stream_fd(handle), addr, addrlen);  
5. } while (r == -1 && errno == EINTR);  
6. // 接続エラー、エラーコードを決定する 7. if (r == -1 && errno != 0) {  
8. // まだ接続中ですが、エラーではありません。接続が完了してイベントが読み取り可能になるまで待機しています。9. if (errno == EINPROGRESS)  
10. ; /* エラーではない */  
11. それ以外の場合 (errno == ECONNREFUSED)  
12. // 接続が拒否されました 13. handle->delayed_error = UV__ERR(ECONNREFUSED);  
14. そうでなければ  
15. UV__ERR(errno) を返します。  
16. }  
17. uv__req_init(handle->loop, req, UV_CONNECT);  
18. req->cb = cb;  
19. req->handle = (uv_stream_t*) ハンドル;  
20. QUEUE_INIT(&req->queue);  
21. // ハンドルにマウントし、書き込み可能なイベントを待機します 22. handle->connect_req = req;  
23. uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);

Libuv はオペレーティング システムを非同期的に呼び出し、リクエストをハンドルにマウントして、書き込み可能なイベントを待機するように登録していることがわかります。接続が失敗すると、uv stream_io コールバックが実行されます。Libuv の処理 (uv stream_io) を見てみましょう。

1. getsockopt(uv__stream_fd(ストリーム)、  
2. ソルソケット、  
3. SO_ERROR、  
4. &エラー、  
5. &errorsize);  
6. エラー = UV__ERR(エラー);  
7. if (req->cb)  
8. req->cb(req, エラー);

エラー情報を取得した後、C++ レイヤーの AfterConnect をコールバックします。

1. ローカル<値> argv[5] = {  
2. Integer::New(env->isolate(), ステータス)、  
3. wrap->object(),  
4. req_wrap->object(),  
5. ブール値::New(env->isolate(), 読み取り可能)、  
6. Boolean::New(env->isolate(), 書き込み可能)  
7. };  
8.    
9. req_wrap->MakeCallback(env->oncomplete_string(), arraysize(argv), argv);

次に、JS レイヤーの oncomplete コールバックを呼び出します。

1. const ex = exceptionWithHostPort(ステータス,  
2. 「接続」  
3. 必要住所、  
4. 要求ポート、  
5. 詳細);  
6. if (詳細) {  
7. ex.localAddress = req.localAddress;  
8. ex.localPort = req.localPort;  
9. }  
10. // ソケットを破棄する  
11. 自己を破棄します(ex);

exceptionWithHostPort はエラー メッセージを作成し、ソケットを破棄して、ex をパラメーターとしてエラー イベントをトリガーします。 uvExceptionWithHostPort の実装を見てみましょう。

1. 関数 uvExceptionWithHostPort(err, syscall, アドレス, ポート) {  
2. const [ code, uvmsg ] = uvErrmapGet(err) || uvUnmappedError;  
3. const message = `${syscall} $[code]: ${uvmsg}`;  
4. details = '' とします。  
5.    
6. if (ポート && ポート > 0) {  
7. details = `${address}:${port}`;  
8. } else if (アドレス) {  
9. details = `${address}`;  
10. }  
11. const tmpLimit = Error.stackTraceLimit;  
12. Error.stackTraceLimit = 0;  
13. const ex = new Error(`${message}${details}`);  
14. Error.stackTraceLimit = tmpLimit;  
15. 例:code = コード;  
16. 例: errno = err;  
17. 例.syscall = syscall;  
18. 例:address = アドレス;  
19. if (ポート) {  
20. 例:port = ポート;  
21。 }  
22. // 呼び出しスタック情報を取得しますが、現在呼び出されている関数 uvExceptionWithHostPort を除外し、スタック フィールドを ex23 に挿入します。Error.captureStackTrace(ex,excludedStackFn ||uvExceptionWithHostPort);  
24. 戻る ex;  
25. }

エラー情報は主にuvErrmapGetを通じて取得されることがわかります。

1. 関数uvErrmapGet(名前) {  
2. uvBinding = lazyUv();  
3. if (!uvBinding.errmap) {  
4. uvBinding.errmap = uvBinding.getErrorMap();  
5. }  
6. uvBinding.errmap.get(name); を返します。  
7. }  
8.    
9. 関数 lazyUv() {  
10. if (!uvBinding) {  
11. uvBinding = 内部バインディング('uv');  
12. }  
13. uvBindingを返します。  
14. }

さらに下を見ていくと、uvErrmapGet は C++ レイヤーの uv モジュールの getErrorMap を呼び出します。

1. void GetErrMap(const FunctionCallbackInfo<Value>& args) {  
2. 環境* env = Environment::GetCurrent(args);  
3. 分離* isolate = env->isolate();  
4. ローカル<Context> context = env->context();  
5.    
6. Local<Map> err_map = Map::New(isolate);  
7. // per_process::uv_errors_map からエラー情報を取得します8. size_t errors_len = arraysize(per_process::uv_errors_map);  
9. // 代入 10. for (size_t i = 0; i < errors_len; ++i) {  
11. // マップのキーは uv_errors_map の各要素の値であり、値は名前とメッセージです。
12. const auto& error = per_process::uv_errors_map[i];  
13. ローカル<値> arr[] = {OneByteString(isolate, error.name),  
14. OneByteString(分離、エラーメッセージ)}; 
15. if (err_map  
16. ->Set(コンテキスト、  
17. 整数::New(分離、エラー値)、  
18. Array::New(isolate, arr, arraysize(arr)))  
19. .IsEmpty()) {  
20. 返却する。  
21。 }  
22。 }  
23。   
24. args.GetReturnValue().Set(err_map);  
25. }

エラー情報は per_process::uv_errors_map に存在することがわかります。uv_errors_map の定義を見てみましょう。

1. 構造体UVError{
2. int 値;
3. const char* 名前;
4. const char* メッセージ;
5. };
6.  
7. 静的定数構造体UVError uv_errors_map[] = {  
8. #define V(名前, メッセージ) {UV_##名前, #名前, メッセージ},  
9. UV_ERRNO_MAP(V)  
10. #undefV  
11. };

UV_ERRNO_MAP マクロは次のように展開されます。

1. {UV_E2BIG, "E2BIG", "引数リストが長すぎます"},  
2. {UV_EACCES、「EACCES」、「権限が拒否されました」}、  
3. {UV_EADDRINUSE、「EADDRINUSE」、「アドレスはすでに使用されています」}、  
4. …

JSレイヤーにエクスポートした結果は次のようになります

1. {  
2. // キーは Libuv によって定義された数値で、実際にはオペレーティング システムの定義をカプセル化します 3. UV_ECONNREFUSED: ["ECONNREFUSED", "接続が拒否されました"],    
4. UV_ECONNRESET: ["ECONNRESET", "ピアによる接続のリセット"]   
5. ...   
6. }

Node.js は最終的にこの情報を組み立てて呼び出し元に返します。これは出力されるエラー メッセージです。では、なぜ ECONNREFUSED なのでしょうか?このエラー コードに対するオペレーティング システムのロジックを見てみましょう。

1. 静的void tcp_reset(構造体sock *sk)  
2. {  
3. スイッチ(sk->sk_state) {  
4. TCP_SYN_SENTの場合:  
5. sk->sk_err = ECONNREFUSED;  
6. 破る;  
7. // ...
8. }  
9.    
10. }

オペレーティング システムは、ソケットに送信された最初のパケットを受信すると、tcp_reset を実行します。ソケットが syn パケットを送信して ack を待機しているときに、fin パケットが受信されると、エラー コードが ECONNREFUSED に設定されることがわかります。出力されるのはこのエラーコードです。

要約する

これで、Node.js のエラー処理プロセス記録に関するこの記事は終了です。より関連性の高い Node.js のエラー処理コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • NodeJs の高メモリ使用量のトラブルシューティング実戦記録
  • Nodejs 組み込み暗号化モジュールを使用してピアツーピアの暗号化と復号化を実現する詳細な説明
  • Node.js の非同期イテレータの詳細な説明
  • Node.js組み込みモジュールの詳細な説明
  • Nodejs モジュール システムのソースコード分析
  • JS と Nodejs におけるイベント駆動型開発についての簡単な説明
  • Nodejs でモジュール fs ファイルシステムを使用する方法
  • Node.js コード実行をバイパスするためのヒントのまとめ
  • Nodejs 探索: シングルスレッドの高並行性の原理を深く理解する
  • Node.js を使用して C# のデータ テーブル エンティティ クラス生成ツールを作成する方法

<<:  Docker+Nginx を使ってシングルページアプリケーションをデプロイする

>>:  MySQLとElasticsearch間のデータ非対称性問題の解決策

推薦する

Tomcat の maxPostSize 設定に関する問題と注意事項

1. maxPostSize を設定する理由は何ですか? tomcat コンテナには送信データのサイ...

Vue の vue.$set() メソッドのソースコード例の詳細な説明

Vue を使用してプロジェクトを開発する過程で、次のような問題によく遭遇します。Vue のデータでオ...

axios でリクエストをキャンセルし、重複リクエストを防ぐ方法について簡単に説明します。

目次序文コア - キャンセルトークン実用的なアプリケーションとパッケージングいくつかの小さな詳細序文...

JS でパブリッシュ サブスクライブ モデルを作成する

目次1. シーン紹介2 コードの最適化2.1 ファンを増やす問題を解決する2.2 作品追加の問題を解...

favicon.ico についていくつか注意点があります (ルートディレクトリに置くのがベストです)

任意のウェブページを開きます。例: http://www.baidu.com/ ブラウザのタブのヘッ...

Mybatisの各SQL文の実行時間の統計

背景最近、面接でデータベース トランザクションについてよく質問されます。通常は、@Transacti...

Linux の MySQL でリモート接続を承認する方法

注意: 他のマシン (IP) は、承認なしではクライアント経由で MySQL データベースに接続でき...

海外のウェブページのカラーマッチング事例20選共有

この記事では、優れた Web ページのカラー マッチングの事例を 20 件集めて紹介します。これらの...

NginxはURLのパスに応じてアップストリームに動的に転送します

Nginx では、URL のパス パラメータに基づいて、到達不可能なアップストリームに動的に転送する...

MySQL インデックスの長さ制限の原理の分析

この記事は主に、MySQL インデックスの長さ制限の原理の分析を紹介します。サンプル コードを通じて...

マウスがカード上に移動したときにフローティング効果を実現する CSS の使用例

原理ホバーしたときに要素に影を設定します: box-shadow で、通常とは異なるスタイルにします...

ウェブデザインに必須のツール: Firefox Web Developer プラグイン CSS ツールセットのチュートリアル

プラグインは Firefox ブラウザにインストールされます。 Web Developer プラグイ...

LinuxベースのLVMシームレスディスク水平拡張の詳細な説明

環境名前財産CPU 5650 円メモリ4Gディスク20G+4TB この時点で、サーバーにはすでに次の...

DockerにMySQL 8.0をインストールする方法

環境: MacOS_Cetalina_10.15.1、Mysql8.0.18、Docker_2.0....

CSS でフロートをクリアするための完全ガイド (要約)

1. 親divは疑似クラスafterとzoomを定義します <スタイル タイプ="...