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間のデータ非対称性問題の解決策

推薦する

Dockerでイメージ情報を表示する方法

この記事では、Dockerでイメージ情報を表示する方法を学ぶ必要があります。 1. imagesコマ...

CSS で background-color を使用して背景画像にマスク効果を追加する 2 つの方法

div で background-color と background-image を同時に設定する...

jQueryはシンプルなカルーセル効果を実装します

みなさんこんにちは。今日はカルーセルの実装についてお話しします。私が作成したカルーセルの効果は次のと...

MySQL 最適化の概要 - クエリエントリの合計数

1. COUNT(*) と COUNT(COL) COUNT(*)は通常、主キーに対してインデックス...

ネイティブWeChatアプレット開発におけるreduxの使用の詳細な説明

前提複雑なシナリオでは、複数の異なるページ間で大量のデータを使用したり変更したりする必要があります。...

2つのVirtualBox仮想ネットワークをブリッジするLinuxブリッジメソッドの手順

この記事は、この時期の「ピーターから奪ってポールに払う」という仕事のスタイルに対する私の不満から生ま...

Vue.js ディレクティブのカスタム命令の詳細な説明

デモコマンドをカスタマイズするVue カスタム ディレクティブの構文は次のとおりです。 Vue.di...

Webpackを使用して複数ページのプログラムを構築するための実装手順

webpack を使用してシングルページのプログラムを構築することは非常に一般的ですが、実際の開発で...

Linux nohup はプログラムをバックグラウンドで実行し、表示します (nohup と &)

1. バックグラウンド実行一般的に、Linux 上のプログラムは .sh ファイル (./sh フ...

Baidu Maps を Web ページに埋め込み、Baidu Maps API を使用してマップをカスタマイズする詳細な手順

ウェブページにBaiduマップを挿入するBaidu Maps を自分の Web ページに追加したい場...

HTMLメタの大きな役割

メタ属性には、name と http-equiv の 2 つがあります。 name 属性は主に、We...

Vue.js ドラッグ可能なテキストボックスコンポーネントの使用方法の詳細な説明

目次コンポーネントの登録コンポーネントの追加ソースコードドラッグ可能なテキスト ボックスでは、ユーザ...

Linux システムの仮想ホストで Swoole Loader 拡張機能を有効にする方法

特記事項: Swoole 拡張機能のみがインストールされ、サーバーはホストにインストールされません。...

よく使われるCSSカプセル化方法の概要

1. pc-reset PCスタイルの初期化 /* 正規化.css */ html{ 行の高さ: 1...