2012-11-05 51 views
14

我想使用Node.js域來捕獲異常。到目前爲止它工作正常,但有一個地方我無法通過域來捕捉異常。在回調中的exception2被捕獲並在domain.on('error')處理程序中處理,但是未捕獲到exception1。奇怪的是,當引發exception1時,它並沒有像我期望的那樣關閉節點。這裏是我的示例應用程序:無法處理使用快遞的node.js域的異常

var domain = require('domain'); 
var request = require('request'); 
var express = require('express'); 

var serverDomain = domain.create(); 
serverDomain.on('error', function(err) { 
    console.log("Server Domain Error: " + err); 
}); 

var app; 

serverDomain.run(function() { 
    app = express(); 
    app.listen(3000); 
}); 

app.use(function(req, res, next) { 

    var reqDomain = domain.create(); 
    reqDomain.add(req); 
    reqDomain.add(res); 
    reqDomain.on('error', function(err) { 
    console.log("Req Domain Error: " + err); 
    reqDomain.dispose(); 
    next(err); 
    }); 

    next(); 
}); 

app.get('/', function(req, res) { 
    var uri = "http://google.com"; 

    exception1.go(); 

    request.get({url:uri, json: {}}, 
    function (error, response, body) { 
     if(response.statusCode === 200) { 
     exception2.go(); 
     res.send('Success getting google response'); 
     } 
    }); 
}); 

要獲得exception2執行,我註釋掉例外1.

回答

18

的問題是,異常期間Connect's routing發生,其中既有try/catcharound its execution,以及一個默認錯誤處理程序,它在非生產模式下運行時輸出堆棧跟蹤詳細信息。由於異常是在Express內處理的,因此它永遠不會到達您的外層以供域處理。

它與exception2的區別在於,'/'路由的處理函數直接由該Connect塊執行,與原來的Express調用相同。第二個異常發生在回調中,在某些I/O操作返回後,因此由源自Node的事件循環I/O處理程序的堆棧執行,因此Express的try/catch不可用於阻止該異常並保存應用服務器。事實上,如果你註釋掉所有的域名,並且旅行exception2它會使Node崩潰。

由於只有未處理的異常纔會路由到域機制,並且由於exception1在其上面的調用堆棧中有一個try/catch可見,所以將處理異常並且不會轉發到域。

+0

因此,根據您的評論,異常將被生產中的域處理程序捕獲? – user1007983

4

@ user1007983

不,在生產中,try/catch語句的處理仍然存在於連接/快遞。解決此問題的方法是在「根」中添加自己的try/catch塊,然後可以在連接發送錯誤響應之前使用它向該域發出「錯誤」事件。

try { 
    // .. code here .. 
} catch (err) { 
    domain.emit('error', err); 
} 

另一種方式來解決它是剛剛從處理程序斷開,像包裹在一個setImmediate塊

0

我試過try/catch塊代碼(這可能不工作,你覺得與異步的方式碼)。我試過幾種不同的方式嘗試使用節點。但我無法處理在第三方庫中引發的異常(sequelize)。爲什麼我會得到例外?那是因爲生成的SQL格式不正確。 (我的錯)。

Anywho,最適合我和我的(小)項目的解決方案是使用forever。讓異常發生,但如果他們這樣做,則再次啓動節點。我懷疑這是最優雅的解決方案,但它適用於我和我的小型項目。

對於較大的項目,結合clustering API的域名可能是一個不錯的選擇。

Winston是另一種選擇。將forever與winston結合起來可能會很酷,所以如果你有異常,你可以有winston email you,這樣你就可以修復代碼。同時,forever會很高興地爲您重新啓動應用程序。

0

我在測試基於域的錯誤處理時遇到了這個問題。

我採用了Issac提出的方法,並做了一些小改動:使用'domain.active'獲取當前活動的域併發出錯誤事件,並使用包裝函數來避免修改所有的我的處理函數:

domainWrapper = function(func) { 
    return function(req, res) { 
     try { 
      return func(req, res); 
     } catch (err) { 
      domain.active.emit('error', err); 
     } 
    } 
} 

然後改變了這樣的事情:

app.get('/jobs', handlerFunc); 

這樣:

app.get('/jobs', domainWrapper(handlerFunc));