この記事では主にNode.jsの非同期例外処理とドメインモジュール解析について紹介します。興味のある方は
非同期例外処理
非同期例外の機能
を参照してください。ノード コールバックの非同期的な性質のため、try catch ですべての例外をキャッチすることは不可能です:
try { process.nextTick(function () { foo.bar(); }); } catch (err) { //can not catch it }
Web サービスの場合、これは実際に非常に期待されています:
//express风格的路由 app.get('/index', function (req, res) { try { //业务逻辑 } catch (err) { logger.error(err); res.statusCode = 500; return res.json({success: false, message: '服务器异常'}); } });
try catch ですべての例外をキャッチできる場合、予期しないエラーが発生したときにコード内でエラーを記録し、わかりやすい方法で呼び出し元に 500 エラーを返すことができます。残念ながら、try catch は非同期状況では例外をキャッチできません。したがって、私たちにできることは次のとおりです。
app.get('/index', function (req, res) { // 业务逻辑 }); process.on('uncaughtException', function (err) { logger.error(err); });
現時点では、エラー ログを記録することはできますが、プロセスは異常終了しません exit が、エラーが見つかったときにフレンドリーなリクエストを返す方法はありません。タイムアウトしてリターン。
ドメイン
ノードv0.8+では、モジュールドメインがリリースされました。このモジュールは、try catch ではできないこと、つまり非同期コールバックで発生する例外をキャッチします。
ということで、上記の無力な例には解決策があるようです:
var domain = require('domain'); //引入一个domain的中间件,将每一个请求都包裹在一个独立的domain中 //domain来处理异常 app.use(function (req,res, next) { var d = domain.create(); //监听domain的错误事件 d.on('error', function (err) { logger.error(err); res.statusCode = 500; res.json({sucess:false, messag: '服务器异常'}); d.dispose(); }); d.add(req); d.add(res); d.run(next); }); app.get('/index', function (req, res) { //处理业务 });
非同期例外を処理するためにミドルウェアの形式でドメインを導入します。もちろん、ドメインは例外を捕捉しましたが、例外によるスタック損失によりメモリ リークが発生する可能性があるため、興味のある学生はドメイン ミドルウェアのプロセスを再起動する必要があります。 。
奇妙な失敗
テストではすべてが正常でしたが、本番環境で正式に使用したところ、突然ドメインが失敗したことがわかりました。非同期例外をキャッチできなかったため、最終的にプロセスが異常終了しました。いくつかの調査の結果、セッションを保存するためのredisの導入が原因であることが最終的に判明しました。
var http = require('http'); var connect = require('connect'); var RedisStore = require('connect-redis')(connect); var domainMiddleware = require('domain-middleware'); var server = http.createServer(); var app = connect(); app.use(connect.session({ key: 'key', secret: 'secret', store: new RedisStore(6379, 'localhost') })); //domainMiddleware的使用可以看前面的链接 app.use(domainMiddleware({ server: server, killTimeout: 30000 }));
このとき、ビジネス ロジック コードで例外が発生したとき、それがドメインによってキャプチャされていないことがわかりました。いくつかの試みの後、最終的に問題が特定されました:
var domain = require('domain'); var redis = require('redis'); var cache = redis.createClient(6379, 'localhost'); function error() { cache.get('a', function () { throw new Error('something wrong'); }); } function ok () { setTimeout(function () { throw new Error('something wrong'); }, 100); } var d = domain.create(); d.on('error', function (err) { console.log(err); }); d.run(ok); //domain捕获到异常 d.run(error); //异常被抛出
奇妙な!どちらも非同期呼び出しですが、前者は捕捉されるのに、後者は捕捉されないのはなぜですか?
ドメイン分析
振り返って、非同期リクエストのキャプチャを可能にするドメインの機能を見てみましょう (コードはノード v0.10.4 からのもので、この部分は急速に変更され、最適化されている可能性があります)。
ノードイベントループメカニズム
ドメインの原理を見る前に、まず nextTick と _tickCallback の 2 つのメソッドを理解する必要があります。
function laterCall() { console.log('print me later'); } process.nextTick(laterCallback); console.log('print me first');
上記のコードでノードを書いたことがある人なら誰でも、 nextTick の機能は、実行のために次のイベント ループに lateCallback を入れることです。 _tickCallback メソッドは非パブリック メソッドです。このメソッドは、現在のタイム ループが終了した後に次のイベント ループを継続するために呼び出されるエントリ関数 です。
言い換えると、ノードはイベント ループのキューを維持し、nextTick がキューに登録され、_tickCallback がデキューされます。ドメインの実装
ノードのイベントループの仕組みを理解した後、ドメインが何をするのかを見てみましょう。 ドメイン自体は実際には EventEmitter オブジェクトであり、キャプチャされたエラーをイベントを通じて配信します。このようにして、研究すると次の 2 つの点に単純化できます: ドメインのエラー イベントがいつトリガーされるか: プロセスは例外をスローし、try catch によってキャッチされません。 processFatal がこの時点でドメイン パッケージ内にある場合は、ドメイン上でエラー イベントがトリガーされ、それ以外の場合は、プロセス上で uncaughtException イベントがトリガーされます。- ドメインがインスタンス化された後、通常、その run メソッド (Web サービスで以前に使用されていたもの) を呼び出して、特定の関数を転送します このドメインの例のパッケージで実行されます。ラップされた関数が実行されると、process.domain のグローバル
変数 がこのドメイン インスタンスを指すようになります。 がこのイベント ループ で例外をスローし、processFatal を呼び出して process.domain が存在することが判明すると、ドメイン上でエラー イベントがトリガーされます。
- require がドメイン モジュールを導入した後、グローバル nextTick と _tickCallback が書き換えられ、いくつかのドメイン関連のコードが挿入されます:
//简化后的domain传递部分代码 function nextDomainTick(callback) { nextTickQueue.push({callback: callback, domain: process.domain}); } function _tickDomainCallback() { var tock = nextTickQueue.pop(); //设置process.domain = tock.domain tock.domain && tock.domain.enter(); callback(); //清除process.domain tock.domain && tock.domain.exit(); } };
这个是其在多个事件循环中传递domain的关键:nextTick入队的时候,记录下当前的domain,当这个被加入队列中的事件循环被_tickCallback启动执行的时候,将新的事件循环的process.domain置为之前记录的domain。这样,在被domain所包裹的代码中,不管如何调用process.nextTick, domain将会一直被传递下去。
当然,node的异步还有两种情况,一种是event形式。因此在EventEmitter的构造函数有如下代码:
if (exports.usingDomains) { // if there is an active domain, then attach to it. domain = domain || require('domain'); if (domain.active && !(this instanceof domain.Domain)) { this.domain = domain.active; } }
实例化EventEmitter的时候,将会把这个对象和当前的domain绑定,当通过emit触发这个对象上的事件时,像_tickCallback执行的时候一样,回调函数将会重新被当前的domain包裹住。
而另一种情况,是setTimeout和setInterval,同样的,在timer的源码中,我们也可以发现这样的一句代码:
if (process.domain) timer.domain = process.domain;
跟EventEmmiter一样,之后这些timer的回调函数也将被当前的domain包裹住了。
node通过在nextTick, timer, event三个关键的地方插入domain的代码,让它们得以在不同的事件循环中传递。
更复杂的domain
有些情况下,我们可能会遇到需要更加复杂的domain使用。
domain嵌套:我们可能会外层有domain的情况下,内层还有其他的domain,使用情景可以在文档中找到
// create a top-level domain for the server var serverDomain = domain.create(); serverDomain.run(function() { // server is created in the scope of serverDomain http.createServer(function(req, res) { // req and res are also created in the scope of serverDomain // however, we'd prefer to have a separate domain for each request. // create it first thing, and add req and res to it. var reqd = domain.create(); reqd.add(req); reqd.add(res); reqd.on('error', function(er) { console.error('Error', er, req.url); try { res.writeHead(500); res.end('Error occurred, sorry.'); } catch (er) { console.error('Error sending 500', er, req.url); } }); }).listen(1337); });
为了实现这个功能,其实domain还会偷偷的自己维持一个domain的stack,有兴趣的童鞋可以在这里看到。
回头解决疑惑
回过头来,我们再来看刚才遇到的问题:为什么两个看上去都是同样的异步调用,却有一个domain无法捕获到异常?理解了原理之后不难想到,肯定是调用了redis的那个异步调用在抛出错误的这个事件循环内,是不在domain的范围之内的。我们通过一段更加简短的代码来看看,到底在哪里出的问题。
var domain = require('domain'); var EventEmitter = require('events').EventEmitter; var e = new EventEmitter(); var timer = setTimeout(function () { e.emit('data'); }, 10); function next() { e.once('data', function () { throw new Error('something wrong here'); }); } var d = domain.create(); d.on('error', function () { console.log('cache by domain'); }); d.run(next);
此时我们同样发现,错误不会被domain捕捉到,原因很清晰了:timer和e两个关键的对象在初始化的时候都时没有在domain的范围之内,因此,当在next函数中监听的事件被触发,执行抛出异常的回调函数时,其实根本就没有处于domain的包裹中,当然就不会被domain捕获到异常了!
其实node针对这种情况,专门设计了一个API:domain.add。它可以将domain之外的timer和event对象,添加到当前domain中去。对于上面那个例子:
d.add(timer); //or d.add(e);
将timer或者e任意一个对象添加到domain上,就可以让错误被domain捕获了。
再来看最开始redis导致domain无法捕捉到异常的问题。我们是不是也有办法可以解决呢?
其实对于这种情况,还是没有办法实现最佳的解决方案的。现在对于非预期的异常产生的时候,我们只能够让当前请求超时,然后让这个进程停止服务,之后重新启动。graceful模块配合cluster就可以实现这个解决方案。
domain十分强大,但不是万能的。希望在看过这篇文章之后,大家能够正确的使用domian,避免踩坑。
【相关推荐】
1. 免费js在线视频教程
3. php.cn独孤九贱(3)-JavaScript视频教程
以上がjsでの非同期例外処理とdomianチュートリアルの使い方の詳しい説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

記事では、JavaScriptライブラリの作成、公開、および維持について説明し、計画、開発、テスト、ドキュメント、およびプロモーション戦略に焦点を当てています。

この記事では、ブラウザでJavaScriptのパフォーマンスを最適化するための戦略について説明し、実行時間の短縮、ページの負荷速度への影響を最小限に抑えることに焦点を当てています。

マトリックスの映画効果をあなたのページにもたらしましょう!これは、有名な映画「The Matrix」に基づいたクールなJQueryプラグインです。プラグインは、映画の古典的な緑色のキャラクター効果をシミュレートし、画像を選択するだけで、プラグインはそれを数値文字で満たされたマトリックススタイルの画像に変換します。来て、それを試してみてください、それはとても面白いです! それがどのように機能するか プラグインは画像をキャンバスにロードし、ピクセルと色の値を読み取ります。 data = ctx.getimagedata(x、y、settings.greasize、settings.greasize).data プラグインは、写真の長方形の領域を巧みに読み取り、jQueryを使用して各領域の平均色を計算します。次に、使用します

この記事では、ブラウザ開発者ツールを使用した効果的なJavaScriptデバッグについて説明し、ブレークポイントの設定、コンソールの使用、パフォーマンスの分析に焦点を当てています。

この記事では、jQueryライブラリを使用してシンプルな画像カルーセルを作成するように導きます。 jQuery上に構築されたBXSLiderライブラリを使用し、カルーセルをセットアップするために多くの構成オプションを提供します。 今日、絵のカルーセルはウェブサイトで必須の機能になっています - 1つの写真は千の言葉よりも優れています! 画像カルーセルを使用することを決定した後、次の質問はそれを作成する方法です。まず、高品質の高解像度の写真を収集する必要があります。 次に、HTMLとJavaScriptコードを使用して画像カルーセルを作成する必要があります。ウェブ上には、さまざまな方法でカルーセルを作成するのに役立つ多くのライブラリがあります。オープンソースBXSLiderライブラリを使用します。 BXSLiderライブラリはレスポンシブデザインをサポートしているため、このライブラリで構築されたカルーセルは任意のものに適合させることができます

データセットは、APIモデルとさまざまなビジネスプロセスの構築に非常に不可欠です。これが、CSVのインポートとエクスポートが頻繁に必要な機能である理由です。このチュートリアルでは、Angular内でCSVファイルをダウンロードおよびインポートする方法を学びます


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 中国語版
中国語版、とても使いやすい

PhpStorm Mac バージョン
最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

EditPlus 中国語クラック版
サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

ホットトピック



