Nodejs異步異常處理domain


前言

程序開發中,最麻煩的事情之一就是異常處理;對於Nodejs程序開發,最麻煩的事情莫過於異步異常處理。

以MVC的多層架構設計角度,異常總是要一層一層向上拋出,最后在客戶端出打印錯誤。但是,Nodejs都是異步異常,try..catch根本就捕捉不到,就會給我們的程序設計帶來不小的麻煩,經常會有未處理的runtime異常,讓整個系統掛掉。

目錄

  1. Nodejs異常處理
  2. Nodejs異步異常處理
  3. domain介紹
  4. domain的API介紹
  5. 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM