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

推薦する

MySQL 5.7 の同時レプリケーションにおける暗黙のバグの分析

序文当社の MySQL オンライン環境のほとんどはバージョン 5.7.18 を使用しています。このバ...

CSS でインラインブロック要素間のギャップを削除するいくつかの方法の詳細な説明

最近、モバイルページを制作する際には、レイアウトにインラインブロック要素がよく使われますが、インライ...

モバイル開発における 1px ラインの理解と解決策

1pxの線が太くなる理由モバイルプロジェクトに取り組むとき、設計図に従って要素ノードのサイズとスタイ...

vue2.x の徹底研究 - h 関数の説明

目次解決、要約: vue プロジェクト。 .vue ファイルのテンプレート内に記述されたコードは、w...

JS の難しさ 同期と非同期、スコープとクロージャ、プロトタイプとプロトタイプ チェーンの詳細な説明

目次JS スリーマウンテンズ同期 非同期同期と非同期の違い範囲、終了関数スコープチェーンブロックスコ...

CSSを使用して中央に固定された2つの列と適応型列を実現する方法

1. 絶対位置とマージンを使用するこの方法の原則は、左側と右側をドキュメントの流れから外れるように配...

MySQL データベースのバックアップをスケジュールするいくつかの方法 (包括的)

目次1. データをバックアップするためのmysqldumpコマンド2. 一般的なmysqldump操...

PostgreSQL データベースにおける varchar、char、text の比較に関する簡単な説明

以下のように表示されます。名前説明する文字可変(n)、varchar(n)長さ制限あり、可変長文字(...

MySQL マルチインスタンス インストール ブート自動起動サービス設定プロセス

1.MySQLの複数インスタンスMySQL マルチインスタンスとは、1 台以上のマシン上で複数の M...

W3C チュートリアル (9): W3C XPath アクティビティ

XPath は、XML ドキュメントの一部を選択するための言語です。 XPath は、XSLT、XQ...

CSSフローティングとフローティング解除について

フロートの定義要素を通常のドキュメント フローから外し、要素を左また​​は右に近づけます。親要素の端...

2級コンピュータ試験のMySQL知識ポイント mysql alterコマンド

テーブル構造を編集するための MySQL の alter コマンドの使用。具体的な内容は以下のとおり...

JavaScript Promise の徹底解説

目次1. Promise とは何ですか? 2. なぜ Promise が存在するのでしょうか? 3つ...

Linux のスケジュールタスク Crontab コマンドの使用に関する詳細な説明と概要

crontab コマンドは、Unix および Linux で定期的な実行命令を設定するために使用され...

React+Antdはテーブルの追加、削除、変更の例を実装します

目次テーブル/index.jsテーブル/モデル/index.jsテーブル/モデル/モジュール/bas...