拿什麼守護你的Node.JS進程: Node出錯崩潰了怎麼辦?

來源:互聯網
上載者:User

標籤:property   function   調用   redis資料庫   expr   一個   獨立   header   比較   

被吐嘈的NodeJS的異常處理


許多人都有這樣一種映像,NodeJS比較快; 但是因為其是單線程,所以它不穩定,有點不安全,不適合處理複雜業務; 它比較適合對並發要求比較高,而且簡單的業務情境。

在Express的作者的TJ Holowaychuk的告別Node.js一文中列舉了以下罪狀:

Farewell NodeJS (TJ Holowaychuk)

?   you may get duplicate callbacks
?   you may not get a callback at all (lost in limbo)
?   you may get out-of-band errors
?   emitters may get multiple “error” events
?   missing “error” events sends everything to hell
?   often unsure what requires “error” handlers
?   “error” handlers are very verbose
?   callbacks suck

其實這幾條主要吐嘈了兩點: node.js錯誤處理很扯蛋,node.js的回調也很扯蛋。

事實上呢?


事實上NodeJS裡程確實有“脆弱”的一面,單線程的某處產生了“未處理的”異常確實會導致整個Node.JS的崩潰退出,來看個例子, 這裡有一個node-error.js的檔案:

var http = require(‘http‘);

var server = http.createServer(function (req, res) {

//這裡有個錯誤,params 是 undefined
var ok = req.params.ok;

res.writeHead(200, {‘Content-Type‘: ‘text/plain‘});
res.end(‘Hello World\n‘);
});

server.listen(8080, ‘127.0.0.1‘);

console.log(‘Server running at http://127.0.0.1:8080/‘);


啟動服務,並在地址欄測試一下發現 http://127.0.0.1:8080/  不出所料,node崩潰了


$ node node-error
Server running at http://127.0.0.1:8080/

c:\github\script\node-error.js:5
var ok = req.params.ok;
^
TypeError: Cannot read property ‘ok‘ of undefined
at Server.<anonymous> (c:\github\script\node-error.js:5:22)
at Server.EventEmitter.emit (events.js:98:17)
at HTTPParser.parser.onIncoming (http.js:2108:12)
at HTTPParser.parserOnHeadersComplete [as onHeadersComplete] (http.js:121:23)
at Socket.socket.ondata (http.js:1966:22)
at TCP.onread (net.js:525:27)



怎麼解決呢?


其實Node.JS發展到今天,如果連這個問題都解決不了,那估計早就沒人用了。

使用uncaughtException


我們可以uncaughtException來全域捕獲未捕獲的Error,同時你還可以將此函數的調用棧列印出來,捕獲之後可以有效防止node進程退出,如:

process.on(‘uncaughtException‘, function (err) {
//列印出錯誤
console.log(err);
//列印出錯誤的調用棧方便調試
console.log(err.stack);
});


這相當於在node進程內部進行守護, 但這種方法很多人都是不提倡的,說明你還不能完全掌控Node.JS的異常。

使用 try/catch


我們還可以在回調前加try/catch,同樣確保線程的安全。

var http = require(‘http‘);

http.createServer(function(req, res) {
try {
handler(req, res);
} catch(e) {
console.log(‘\r\n‘, e, ‘\r\n‘, e.stack);
try {
res.end(e.stack);
} catch(e) { }
}
}).listen(8080, ‘127.0.0.1‘);

console.log(‘Server running at http://127.0.0.1:8080/‘);

var handler = function (req, res) {
//Error Popuped
var name = req.params.name;

res.writeHead(200, {‘Content-Type‘: ‘text/plain‘});
res.end(‘Hello ‘ + name);
};


這種方案的好處是,可以將錯誤和調用棧直接輸出到當前發生的網頁上。

整合到架構中


標準的HTTP響應處理會經曆一系列的Middleware(HttpModule),最終到達Handler,如所示:




這些Middleware和Handler在NodeJS中都有一個特點,他們都是回呼函數,而回呼函數中是唯一會讓Node在運行時崩潰的地方。根據這個特點,我們只需要在架構中整合一處try/catch就可以相對完美地解決異常問題,而且不會影響其它使用者的請求request。

事實上現在的NodeJS WEB架構幾乎都是這麼做的,如OurJS開源部落格所基於的WebSvr

就有這麼一處異常處理代碼:

Line: 207

  try {
    handler(req, res);
  } catch(err) {
    var errorMsg
      = ‘\n‘
      + ‘Error ‘ + new Date().toISOString() + ‘ ‘ + req.url
      + ‘\n‘
      + err.stack || err.message || ‘unknow error‘
      + ‘\n‘
      ;

    console.error(errorMsg);
    Settings.showError
      ? res.end(‘<pre>‘ + errorMsg + ‘</pre>‘)
      : res.end();
  }


那麼不在回調中產生的錯誤怎麼辦?不必擔心,其實這樣的node程式根本就起不起來。

此外node內建的 cluster 也有一定的容錯能力,它跟nginx的worker很類似,但消耗資源(記憶體)略大,編程也不是很方便,OurJS並沒有採用此種設計,未來如果流量達到一定程度,單個進程無法滿足要求時,或採用多個伺服器(VMs),起多個相互獨立的node websvr進程,將需要共用的session存放在一處統一的redis資料庫中。

守護NodeJS進程和記錄錯誤記錄檔


現在已經基本上解決了Node.JS因異常而崩潰的問題,不過任何平台都不是100%可靠的,還有一些錯誤是從Node底層拋出的,有些異常try/catch和uncaughtException都無法捕獲。之前在運行ourjs的時侯,會偶爾碰到底層拋出的檔案流讀取異常,這就是一個底層libuv的BUG,node.js在0.10.21中進行了修複。

面對這種情況,我們就應該為nodejs應用添加守護進程,讓NodeJS遭遇異常崩潰以後能馬上複活。

另外,還應該把這些產生的異常記錄到日誌中,並讓異常永遠不再發生。

使用node來守護node


node-forever 提供了守護的功能和LOG日誌記錄功能。

安裝非常容易

[sudo] npm install forever


使用也很簡單

$ forever start simple-server.js
$ forever list
  [0] simple-server.js [ 24597, 24596 ]


還可以看日誌

forever -o out.log -e err.log my-script.js

 

使用shell啟動指令碼守護node


使用node來守護的話資源開銷可能會有點大,而且也會略顯複雜,OurJS直接在開機啟動指令碼來進程線程守護。

如在debian中放置的 ourjs 開機開機檔案: /etc/init.d/ourjs

這個檔案非常簡單,只有啟動的選項,守護的核心功能是由一個無限迴圈 while true; 來實現的,為了防止過於密集的錯誤阻塞進程,每次錯誤後間隔1秒重啟服務

WEB_DIR=‘/var/www/ourjs‘
WEB_APP=‘svr/ourjs.js‘

#location of node you want to use
NODE_EXE=/root/local/bin/node

while true; do
    {
        $NODE_EXE $WEB_DIR/$WEB_APP config.magazine.js
        echo "Stopped unexpected, restarting \r\n\r\n"
    } 2>> $WEB_DIR/error.log
    sleep 1
done


錯誤記錄檔記錄也非常簡單,直接將此進程式控制制台當中的錯誤輸出到error.log檔案即可: 2>> $WEB_DIR/error.log  這一行, 2 代表 Error。

拿什麼守護你的Node.JS進程: Node出錯崩潰了怎麼辦?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.