前言
程序開發中,最麻煩的事情之一就是異常處理;對於Nodejs程序開發,最麻煩的事情莫過於異步異常處理。
以MVC的多層架構設計角度,異常總是要一層一層向上拋出,最后在客戶端出打印錯誤。但是,Nodejs都是異步異常,try..catch根本就捕捉不到,就會給我們的程序設計帶來不小的麻煩,經常會有未處理的runtime異常,讓整個系統掛掉。
目錄
- Nodejs異常處理
- Nodejs異步異常處理
- domain介紹
- domain的API介紹
- domain異步異常特例
1. Nodejs同步異常處理
系統環境
- win7 64bit
- Nodejs:v0.10.5
- Npm:1.2.19
創建項目
~ D:\workspace\javascript>mkdir nodejs-domain && cd nodejs-domain
新建文件:sync.js,模擬同步異常的處理
~ vi sync.js function sync_error() { var r = Math.random() * 10; console.log("random num is " + r); if (r > 5) { throw new Error("Error: random num" + r + " > 5"); } } setInterval(function () { try { sync_error(); } catch (err) { console.log(err); } }, 1000)
運行程序
~ D:\workspace\javascript\nodejs-domain>node sync.js random num is 1.067440479528159 random num is 6.284254263155162 [Error: Error: random num6.284254263155162 > 5] random num is 8.445568478200585 [Error: Error: random num8.445568478200585 > 5] random num is 2.79862868366763 random num is 5.452311446424574 [Error: Error: random num5.452311446424574 > 5] random num is 3.725348354782909 random num is 7.590636070817709 [Error: Error: random num7.590636070817709 > 5] random num is 9.584896392188966 [Error: Error: random num9.584896392188966 > 5] random num is 3.63708304008469 random num is 5.747077965643257 [Error: Error: random num5.747077965643257 > 5] random num is 1.0771577199921012 random num is 8.898805833887309 [Error: Error: random num8.898805833887309 > 5] random num is 6.59792885184288 [Error: Error: random num6.59792885184288 > 5] random num is 1.8532328261062503 random num is 3.6028534593060613 random num is 2.7523675211705267 random num is 1.598257850855589
通過try..catch可以很好的抓到同步程序的異常。
2. Nodejs異步異常處理
新建文件:async.js,模擬異步異常處理
~ vi async.js function async_error() { setTimeout(function(){ var r = Math.random() * 10; console.log("random num is " + r); if (r > 5) { throw new Error("Error: random num" + r + " > 5"); } },10) } setInterval(function () { try { async_error(); } catch (err) { console.log(err); } }, 1000)
運行程序
~ D:\workspace\javascript\nodejs-domain\sync.js:5 throw new Error("Error: random num" + r + " > 5"); ^ Error: Error: random num9.974474618211389 > 5 at trycatch (D:\workspace\javascript\nodejs-domain\sync.js:5:15) at Timer. (D:\workspace\javascript\nodejs-domain\sync.js:10:5) at Timer.timer.ontimeout (timers.js:247:14)
try..catch,無法捕捉異步異常!
修改async.js, 通過process.on()打印錯誤信息。
~ vi async.js function async_error() { setTimeout(function(){ var r = Math.random() * 10; console.log("random num is " + r); if (r > 5) { throw new Error("Error: random num" + r + " > 5"); } },10) } setInterval(function () { try { async_error(); } catch (err) { console.log(err); } }, 1000) process.on('uncaughtException', function (err) { console.log(err); });
運行程序
~ D:\workspace\javascript\nodejs-domain>node async.js random num is 9.33843155624345 [Error: Error: random num9.33843155624345 > 5] random num is 7.894433259498328 [Error: Error: random num7.894433259498328 > 5] random num is 2.532815719023347 random num is 6.0961083066649735 [Error: Error: random num6.0961083066649735 > 5] random num is 5.138748907484114 [Error: Error: random num5.138748907484114 > 5]
通過process.on(‘uncaughtException’)的內置函數,雖然我們可以記錄下這個錯誤的日志,而且進程也不會異常退出,但是我們是沒有辦法對發現錯誤的請求友好返回的,只能夠讓它超時返回。
3. domain介紹
node在v0.8+版本的時候,發布了一個模塊domain。這個模塊做的就是try catch所無法做到的:捕捉異步回調中出現的異常。
domain模塊,把處理多個不同的IO的操作作為一個組。注冊事件和回調到domain,當發生一個錯誤事件或拋出一個錯誤時,domain對象 會被通知,不會丟失上下文環境,也不導致程序錯誤立即推出,與process.on(‘uncaughtException’)不同。
domain的發布頁http://nodejs.org/api/domain.html
用domain捕捉異常,新建文件domain.js
~ vi domain.js var domain = require('domain'); function sync_error() { var r = Math.random() * 10; console.log("sync num is " + r); if (r > 5) { throw new Error("sync: random num" + r + " > 5"); } } function async_error() { setTimeout(function(){ var r = Math.random() * 10; console.log("async num is " + r); if (r > 5) { throw new Error("async: random num" + r + " > 5"); } },10) } var d = domain.create(); d.on('error',function(err){ console.log(err); }); setInterval(function () { d.run(sync_error); d.run(async_error); }, 1000)
運行程序
~ D:\workspace\javascript\nodejs-domain>node domain.js sync num is 8.492766928393394 { [Error: sync: random num8.492766928393394 > 5] domain: { domain: null, _events: { error: [Function] }, _maxListeners: 10, members: [] }, domainThrown: true } sync num is 4.991524459328502 async num is 7.5735537661239505 { [Error: async: random num7.5735537661239505 > 5] domain: { domain: null, _events: { error: [Function] }, _maxListeners: 10, members: [] }, domainThrown: true } sync num is 4.626072463579476 async num is 9.48660139227286 { [Error: async: random num9.48660139227286 > 5] domain: { domain: null, _events: { error: [Function] }, _maxListeners: 10, members: [] }, domainThrown: true } sync num is 2.3057156521826982 async num is 4.5645097037777305 sync num is 2.0251641585491598 async num is 7.712894310243428 { [Error: async: random num7.712894310243428 > 5] domain: { domain: null, _events: { error: [Function] }, _maxListeners: 10, members: [] }, domainThrown: true }
我們發現domain,可以同時捕捉到同步異常(sync)和異步異常(async)。
4. domain的API介紹
基本概念
- 隱式綁定: 把在domain上下文中定義的變量,自動綁定到domain對象
- 顯式綁定: 把不是在domain上下文中定義的變量,以代碼的方式綁定到domain對象
API介紹
- domain.create(): 返回一個domain對象
- domain.run(fn): 在domain上下文中執行一個函數,並隱式綁定所有事件,定時器和低級的請求。
- domain.members: 已加入domain對象的域定時器和事件發射器的數組。
- domain.add(emitter): 顯式的增加事件
- domain.remove(emitter): 刪除事件
- domain.bind(callback): 以return為封裝callback函數
- domain.intercept(callback): 同domain.bind,但只返回第一個參數
- domain.enter(): 進入一個異步調用的上下文,綁定到domain
- domain.exit(): 退出當前的domain,切換到不同的鏈的異步調用的上下文中。對應domain.enter()
- domain.dispose(): 釋放一個domain對象,讓node進程回收這部分資源
5. domain異步異常特例
下面這個例子,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('Receive data error!'); }); } var d = domain.create(); d.on('error', function (err) { console.log(err); }); d.run(next);
運行程序
~ D:\workspace\javascript\nodejs-domain\special.js:12 throw new Error('Receive data error!'); ^ Error: Receive data error! at EventEmitter. (D:\workspace\javascript\nodejs-domain\special.js:12:15) at EventEmitter.g (events.js:175:14) at EventEmitter.emit (events.js:92:17) at null._onTimeout (D:\workspace\javascript\nodejs-domain\special.js:7:7) at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
如果timer和e兩個關鍵的對象在初始化的時候都時沒有在domain的范圍之內,因此,當在next函數中監聽的事件被觸發,執行拋出異常的回調函數時,其實根本就沒有處於domain的包裹中,就不會被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('Receive data error!'); }); } var d = domain.create(); d.on('error', function (err) { console.log(err); }); d.add(e); d.add(timer); d.run(next);
增加e和timer到domain的范圍內,運行程序
~ D:\workspace\javascript\nodejs-domain>node special.js { [Error: Receive data error!] domain: { domain: null, _events: { error: [Function] }, _maxListeners: 10, members: [ [Object], [Object] ] }, domainThrown: true }
domain特例代碼摘自:http://cnodejs.org/topic/516b64596d38277306407936
通過domain模塊,我們就可以好好設計Nodejs系統的異常管理了。
文章轉載自:http://blog.fens.me/nodejs-core-domain/ 看到不錯的文章,自己又能看懂的果斷轉載,學習並收錄下,o(∩_∩)o