王龑 — APRIL 08, 2015express
不少 NodeJS 的開發者在抱怨異常處理太麻煩,咱們會經過一些列博客梳理一下NodeJS中常見的異常處理的手段。
和大多數編程語言同樣,在 NodeJS 裏能夠經過throw
拋出一個異常:apache
throw new Error('Catch me');
爲了捕獲這個異常須要把代碼包在Try Catch
中:編程
try{ throw new Error('Catch me'); }catch(e){ // error captured }
然而,因爲 NodeJS 的異步特性,上述代碼只需稍加改造就會失效:服務器
try{ process.nextTick(function my_app(){ throw new Error('Catch me'); }) }catch(e){ // never called }
在現實世界裏,異常老是會產生在某個模塊中。所謂模塊就是能完成一個功能的單元,即便是一個簡單的函數也能夠被看作一個模塊。隨着項目代碼行數增多,異步嵌套的複雜性增強,常常會有異常沒捕獲的狀況發生。一個沒有很強健壯性的 NodeJS 應用,會由於一個未捕獲的異常就整個掛掉,致使服務不可用。要改變你們以爲NodeJS是脆弱的這個認識,須要開發者加深對這門語言異常處理機制的瞭解。app
uncaughtException
實際上是 NodeJS 進程的一個事件。若是進程裏產生了一個異常而沒有被任何Try Catch
捕獲會觸發這個事件。爲了簡化問題,咱們仍是先看看同步狀況下的例子。異步
function external() { throw new Error('Catch me'); } function internal() { external(); } internal(); //error will be thrown
在命令行裏執行這個程序,腳本會在拋出異常的那一行中斷。接下來,因爲沒有Try Catch
,異常會一直冒泡直到事件循環爲止,而NodeJS對異常的默認處理很是簡單,處理的代碼 相似 於:編程語言
function _MyFatalException(err){ if(!process.emit('uncaughtException',err)){ console.error(err.stack); process.emit('exit',1); } }
NodeJS對於未捕獲異常的默認處理是: - 觸發 uncaughtException
事件 - 若是 uncaughtException 沒有被監聽,那麼 - 打印異常的堆棧信息 - 觸發進程的 exit 事件函數
若是你正在用 NodeJS 開發服務器,那麼你確定不但願偶然的一個異常讓整個服務器掛掉。那麼是否是隻要監聽了 uncaughtException
就能夠阻止服務器的進程退出呢? 答案是能夠,可是不要這麼作!。看這個例子:oop
var express = require('express'); function external(cb) { process.nextTick(function () { throw new Error(); cb.call(null, 'sunny'); }) } var app = express(); app.get('/weather', function (req, res) { external(function (data) { res.end('Weather of Beijing is ' + data); }) }) app.listen(8018); function noop(){} process.on('uncaughtException', noop)
上面這個例子假設用戶訪問站點的時候能夠看到當地的天氣,咱們用 apache2-utils
來模擬請求ui
ab -n 1000 -c 20 http://localhost:8018/weather
糟糕!請求一直在等待,內存上漲。緣由在於res.end
永遠不會執行,現有的I/O
處於等待的狀態,已經開闢的資源不只不會被釋放,並且服務器還在不知疲倦地接受新的用戶請求。
在 NodeJS 中處理異常是代價高昂的,並且一不當心就會致使內存泄露和讓應用程序處於不穩定的狀態。爲了提升健壯性,咱們能夠用Cluster
模式,由之而來的推薦作法是: - 針對發生異常的請求返回一個錯誤代碼 - 出錯的Worker再也不接受新的請求 - 退出關閉Worker進程