NodeJS 如何异常处理 uncaughtException - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
OneAPM

NodeJS 如何异常处理 uncaughtException

  •  9
     
  •   OneAPM 2015 年 6 月 9 日 3639 次点击
    这是一个创建于 3972 天前的主题,其中的信息可能已经有所发展或是发生改变。

    很多 NodeJS 的开发者在抱怨异常处理太麻烦,我们会通过一些列博客梳理一下NodeJS中常见的异常处理的手段。
    和大多数编程语言一样,在 NodeJS 里可以通过throw抛出一个异常:

    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是脆弱的这个认识,需要开发者加深对这门语言异常处理机制的了解。

    uncaughtException

    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 就可以阻止服务器的进程退出呢? 答案是可以,但是不要这么做!。看这个例子:

    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 来模拟请求

    ab -n 1000 -c 20 http://localhost:8018/weather

    糟糕!请求一直在等待,内存上涨。原因在于res.end 永远不会执行,现有的I/O处于等待的状态,已经开辟的资源不仅不会被释放,而且服务器还在不知疲倦地接受新的用户请求。

    在 NodeJS 中处理异常是代价高昂的,而且一不小心就会导致内存泄露和让应用程序处于不稳定的状态。为了提高健壮性,我们可以用Cluster模式,由之而来的推荐做法是: - 针对发生异常的请求返回一个错误代码 - 出错的Worker不再接受新的请求 - 退出关闭Worker进程


    本文系OneAPM工程师编译整理,想阅读更多技术文章,请访问OneAPM官方技术博客

    4 条回复    2015-07-15 16:12:35 +08:00
    YuJianrong
        1
    YuJianrong  
       2015 年 6 月 10 日
    文章不错不过读起来太伤眼……
    OneAPM
        2
    OneAPM  
    OP
       2015 年 6 月 10 日
    @YuJianrong 下次我们改进下排版 :P
    jiangzhuo
        3
    jiangzhuo  
       2015 年 6 月 24 日
    怎不提Domain呢
    adoyle
        4
    adoyle  
       2015 年 7 月 15 日
    问一个问题,当服务端发生 uncaughtException,而且是因为请求某些路由而必定发生的,有下面两种处理方式,你会选哪个?

    1. 记录异常,通知管理员,不退出进程。让服务端继续跑,尽可能多的响应其他请求。

    2. 记录异常,通知管理员,重启进程。但是只要触发到异常就会立刻重启,有可能很频繁,会导致服务端一段时间内无法响应请求。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2891 人在线   最高记录 6679   &bsp;   Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 38ms UTC 01:50 PVG 09:50 LAX 18:50 JFK 21:50
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86