捕获throwable还是exception?

上周发生了一个BUG,用了一天的时间才解决,记录下过程。

    public int getIceTea(int teaId) {
        logger.info("getIceTea|access|teaId:" + teaId);  // 1
        try {
            Object obj = getFromCache(teaId);
            if (obj == null) {
                obj = getFromDb(teaId);
            }
            logger.info("getIceTea|result|tea:" + obj); // 2
        } catch (Exception e) {
            logger.error("getIceTea|error",e); // 3
            return -1;
        }
        return 0;
    }

一切要从上面这段代码开始说起:这是一个RPC方法,原来的代码已在线上运行了一段时间,随着调用量的增加,希望增加一个缓存层以便提高性能,于是增加了getFormCache()函数,其中的缓存实现使用Guava的LoadingCache。大功告成,打包、发布到测试环境,进行接口测试,一切正常。继续发布到正式环境,进行接口测试,不幸的事情发生了,接口报错。“报错嘛,还好还好,我有完备的日志”,打开日志,发现只记录了1的日志,2和3没有。这我就不能理解了,在我认为,1和2或者1和3必须是成对出现的,只有1是什么鬼。
没有想到好的方法,硬着头皮从getFromCache()层层加入日志,不断调试。偶然想到,会不会抛出了更高层级的异常?于是将Exception更换其父类为Throwable,最终发现罪魁祸首,Throwable的另一个子类Error

    getIceTea|error:
    java.lang.NoSuchMethodError: com.google.common.base.Platform.systemNanoTime()J
    at ...

在IDE里搜索Platform类,发现guavagoogle-collections两个jar包里有相同名字和相同包名的Platform

Platform源码对比

其中google-collections里面的没有systemNanoTime()方法,可知在测试环境虚拟机正确加载了guava中的Platform类所以正常,而正式环境加载了google-collections中的Platform类所以抛出NoSuchMethodError。那么虚拟机加载jar包的顺序是怎样的呢?官方文档里有这样的描述:

The order in which the JAR files in a directory are enumerated in the expanded class path is not specified and may vary from platform to platform and even from moment to moment on the same machine. A well-constructed application should not depend upon any particular order. If a specific order is required, then the JAR files can be enumerated explicitly in the class path

翻译为中文,即:虚拟机加载类路径目录中的各个jar包的顺序是不确定的,在不同平台上不同,甚至同一机器的不同时刻也不相同。一般情况下,JAVA应用不应该依赖于jar包加载顺序。如果必须依赖jar包加载顺序,则应该在类路径CLASS PATH中显式的指定。
可知,开头的代码在测试环境中正常也只是偶然,极有可能下次启动,接口就会发生异常。尝试重启了几次,证明事实正是如此:测试环境也发生了接口异常。找到了原因,解决BUG就很容易了,由于新引入了guava包,google-collections就变得冗余了,删去该包即可。
回顾整个过程,解决这个BUG的困难不在于根据NoSuchMethodError查出jar包污染,而在于定位到异常的源头,也就是,catch (Throwable t) or catch (Exception e)?查阅JDK文档,对Error类有这样的注释:

A method is not required to declare in its throws clause any subclasses of Error that might be thrown during the execution of the method but not caught, since these errors are abnormal conditions that should never occur.

即,JAVA方法不需要在throws子句中声明方法在执行过程中抛出的任何Error及其子类也不应该捕获,因为Error是永远不会发生的异常条件。也就是说,需要捕获RuntimeExceptionChecked Exception,但是永远不要捕获Error。文档中的提法,是基于这样的考虑:在应用执行过程中如果发生了Error比如OutOfMemoryError,那么意味着程序已经不可能再做任何恢复,此时终止执行、退出程序、及时人工介入处理才是合理的做法。
但是,Never say never,某些情况下捕获Error是很有必要的。想象这样的情况,如果你在开发一个Eclipse类似的App,你设计了插件机制可以由第三方来编写插件扩展功能,当其中某个插件加载错误,抛出比如前文所述的NoSuchMethodError时,我们期待的是提示插件加载失败而不是退出Eclipse,此时捕获包括Error在内的Throwable就显得很有必要。此外,当编写一些框架级别的程序,在代码的最底层捕获Throwable也很有必要,这样才不会使框架崩溃。比如,Netty中线程NioEventLoop正是如此处理:

    for (;;) {
        try {
            // process
        }catch (Throwable t) {
            handleLoopException(t);
        }
    }

那么,是否需要每次都捕获Throwable呢?这是最安全的方法,但性能不高,并不提倡。折中的做法是:在最底层代码捕获Throwable,其他层级代码捕获Exception
最后回到开始的问题,抛出Error的方法并不是RPC框架的底层代码,所以不应该捕获Throwable。那么,框架的底层是否处理了Throwable呢,答案是肯定的,和Netty类似,简单的记录日志而不进行任何其他处理。所以,再次遇到这种问题时,需要关注框架级的日志,本例中由于框架日志和普通日志并不在同一路径,导致忽略查看框架日志。
又回到了原点,貌似不一样了。。。

附收集到的关于ExceptionError的一些看法:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,599评论 18 139
  • 原文链接:http://www.dropwizard.io/1.2.0/docs/getting-started....
    Lance_Xu阅读 884评论 0 0
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,748评论 6 342
  • 一丝风/一滴雨/一粒种子/还有一拈尘土//小苗/弯弯的/唱着风一样的歌/啍着雨一样的曲/露珠在跳荡//记得你顽皮的...
    冰凉小小手阅读 276评论 0 0
  • 楼上的人 今晚放的屁 被我一个不漏地 稳稳接住
    向日葵爱呀爱太阳阅读 59评论 0 0