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

推薦する

JavaScript 配列の include と Reduce の基本的な使用法

目次序文配列.プロトタイプ.includes文法パラメータ戻り値例配列プロトタイプの削減文法パラメー...

Centos8.3、dockerデプロイメントspringbootプロジェクトの実際のケース分析

導入現在、k8s は非常に人気があり、それについて学ぶために本を購入しました。しかし、k8s では数...

MysqlチューニングExplainツールの詳細な説明と実践的な演習(推奨)

MySQL チューニング ツールの詳細な説明と実践的な演習の説明 ツールの紹介の説明 分析例の説明...

MySQL の frm ファイルからテーブル構造を復元する 3 つの方法 [推奨]

mysql が正常に実行されている場合、テーブル構造を表示することは難しくありません。しかし、場合...

Mysql の一時テーブルとパーティションテーブルの違いの詳細な説明

一時テーブルとメモリテーブルメモリ テーブルとは、メモリ エンジンを使用するテーブルを指します。テー...

CSS3 で虫眼鏡効果を模倣するいくつかの方法の原理の分析

記事のタイトルが「模造虫眼鏡」なのはなぜですか?今日お話ししたいのは、一般的に言われているような、マ...

MySQL 8.0.13 で日付を 0000-00-00 00:00:00 に設定すると発生する問題を解決する

データベース操作を学び始めたばかりです。今日、データを保存していたところ、エラーが発生していることに...

純粋な CSS で中空効果を実現するためのサンプルコード

私は最近、空洞化効果について研究しました。背景クリップ: テキスト背景はテキストの前景色にクリップさ...

HTMLの最適化によりWebページの速度が向上

明らかな HTML、隠された「公開スクリプト」 Web ページのダウンロード時間を短縮する鍵は、フ...

Echarts 凡例コンポーネントのプロパティとソース コード

凡例コンポーネントは、ECharts でよく使用されるコンポーネントです。シリーズ マーカーの名前を...

Ubuntu サーバーで MySQL を設定し、リモート接続を実装する方法

サーバー: Ubuntu Server 16.04 LSSクライアント: Ubuntu 16.04 ...

vsFTP 3.0.3 のコンパイルとインストールの詳細な分析

脆弱性の詳細VSFTP は、GPL に基づいてリリースされた Unix ライクなシステムで使用される...

Linux で ffmpeg をインストールするための詳細なチュートリアル

1. CentOS Linuxにffmpegをインストールする1.ダウンロードして解凍する http...

js のプロトタイプ、プロトタイプ オブジェクト、プロトタイプ チェーンの包括的な分析

目次プロトタイプを理解するプロトタイプオブジェクトを理解するインスタンスプロパティとプロトタイププロ...

div 要素に終了タグがないため、Web ページを開くことができません

最初は速度の問題だと思ったので、その後、すべての画像リンク リクエストをクロスサイト接続ではなくサイ...