使用 connect-domain 捕獲非同步呼叫中出現的異常

來源:互聯網
上載者:User

之前經常有同學會問到怎麼有些異常無法捕獲到呢?

雖然 connect 已經在 handler 外層加了 try catch ,還是無法捕獲非同步呼叫中產生的異常。

現狀

例如最簡單的 helloworld.js 代碼

var connect = require('connect');var app = connect().use(function (req, res, next) {  if (req.url === '/sync_error') {    throw new Error('sync error');  }  if (req.url === '/async_error') {    return process.nextTick(function () {      mk2.haha();    });  }  res.end('hello, ' + req.method + ' ' + req.url);});process.on('uncaughtException', function (err) {  console.error(err);});app.listen(1985);

使用 curl 來簡單測試一下

$ curl localhost:1985/foohello, GET /foo$ curl localhost:1985/sync_errorError: sync error    at Object.handle ( .../app.js:33:11)    ....

好像還挺正常的,異常也被捕獲了。 繼續測試

$ curl localhost:1985/async_error

沒任何輸出了吧?從伺服器端控制台看到這樣的輸出: 證明觸發了 uncaughtException 事件。

[ReferenceError: mk2 is not defined]

雖然我們能通過 uncaughtException 事件捕獲到非同步呼叫中產生的異常,但是我們沒辦法返回 HTTP 500 異常響應給調用者。 調用者也只能一直hold住,直到請求逾時。

這是非常不好的做法,因為請求量很大,服務進程記憶體會暴漲,會導致進程超出記憶體限制,非正常退出。

改進

任何時候,如果伺服器端出現異常,我們就應該返回HTTP 500 告訴調用者伺服器異常了。

非同步呼叫產生的異常也應該如此處理。

nodejs@0.8 開始,增加了 domain 模組來協助我們更好地處理異常。

讓我們使用 connect-domain 來改進一下之前的 helloworld.js:

var connect = require('connect');var connectDomain = require('connect-domain');var app = connect().use(connectDomain()) // just using connect-domian middleware.use(function (req, res, next) {  if (req.url === '/sync_error') {    throw new Error('sync error');  }  if (req.url === '/async_error') {    return process.nextTick(function () {      mk2.haha();    });  }  res.end('hello, ' + req.method + ' ' + req.url);});process.on('uncaughtException', function (err) {  console.error(err);});app.listen(1984);

再看看 connect-domain 的實現,非常簡單:

var domain = require('domain');module.exports = function (handler) {  return function domainMiddleware(req, res, next) {    var reqDomain = domain.create();    res.on('close', function () {      reqDomain.dispose();    });    reqDomain.on('error', function (err) {      if (typeof handler === 'function') {        handler(err, req, res, next);      } else {        next(err);      }    });    reqDomain.run(next);  };};

重複剛才的 curl 測試:

$ curl localhost:1984/foohello, GET /foo$ curl localhost:1984/sync_errorError: sync error    at Object.handle ( .../app.js:33:11)    ....$ curl localhost:1984/async_errorReferenceError: mk2 is not defined    at .../app.js:24:7    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

所有請求都有響應了。

伺服器處理序的輸出:

{ [ReferenceError: mk2 is not defined]  domain_thrown: true,  domain:    { domain: null,     _events: { error: [Function] },     _maxListeners: 10,     members: [] } }

奇怪的連 uncaughtException 事件也觸發了。這按我的思維來說,不應該觸發才對的。

@isaacs 最新的提交已經修複此問題: https://github.com/joyent/node/issues/4375#issuecomment-11691069

總結

應該沒人再說 nodejs 無法捕獲非同步呼叫中出現的異常了吧?

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.