捕獲throwable還是exception?

上周發(fā)生了一個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方法,原來的代碼已在線上運行了一段時間,隨著調(diào)用量的增加,希望增加一個緩存層以便提高性能,于是增加了getFormCache()函數(shù),其中的緩存實現(xiàn)使用Guava的LoadingCache。大功告成,打包、發(fā)布到測試環(huán)境,進行接口測試,一切正常。繼續(xù)發(fā)布到正式環(huán)境,進行接口測試,不幸的事情發(fā)生了,接口報錯?!皥箦e嘛,還好還好,我有完備的日志”,打開日志,發(fā)現(xiàn)只記錄了1的日志,2和3沒有。這我就不能理解了,在我認為,1和2或者1和3必須是成對出現(xiàn)的,只有1是什么鬼。
沒有想到好的方法,硬著頭皮從getFromCache()層層加入日志,不斷調(diào)試。偶然想到,會不會拋出了更高層級的異常?于是將Exception更換其父類為Throwable,最終發(fā)現(xiàn)罪魁禍首,Throwable的另一個子類Error

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

在IDE里搜索Platform類,發(fā)現(xiàn)guavagoogle-collections兩個jar包里有相同名字和相同包名的Platform

Platform源碼對比

其中google-collections里面的沒有systemNanoTime()方法,可知在測試環(huán)境虛擬機正確加載了guava中的Platform類所以正常,而正式環(huán)境加載了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應(yīng)用不應(yīng)該依賴于jar包加載順序。如果必須依賴jar包加載順序,則應(yīng)該在類路徑CLASS PATH中顯式的指定。
可知,開頭的代碼在測試環(huán)境中正常也只是偶然,極有可能下次啟動,接口就會發(fā)生異常。嘗試重啟了幾次,證明事實正是如此:測試環(huán)境也發(fā)生了接口異常。找到了原因,解決BUG就很容易了,由于新引入了guava包,google-collections就變得冗余了,刪去該包即可。
回顧整個過程,解決這個BUG的困難不在于根據(jù)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子句中聲明方法在執(zhí)行過程中拋出的任何Error及其子類也不應(yīng)該捕獲,因為Error是永遠不會發(fā)生的異常條件。也就是說,需要捕獲RuntimeExceptionChecked Exception,但是永遠不要捕獲Error。文檔中的提法,是基于這樣的考慮:在應(yīng)用執(zhí)行過程中如果發(fā)生了Error比如OutOfMemoryError,那么意味著程序已經(jīng)不可能再做任何恢復(fù),此時終止執(zhí)行、退出程序、及時人工介入處理才是合理的做法。
但是,Never say never,某些情況下捕獲Error是很有必要的。想象這樣的情況,如果你在開發(fā)一個Eclipse類似的App,你設(shè)計了插件機制可以由第三方來編寫插件擴展功能,當其中某個插件加載錯誤,拋出比如前文所述的NoSuchMethodError時,我們期待的是提示插件加載失敗而不是退出Eclipse,此時捕獲包括Error在內(nèi)的Throwable就顯得很有必要。此外,當編寫一些框架級別的程序,在代碼的最底層捕獲Throwable也很有必要,這樣才不會使框架崩潰。比如,Netty中線程NioEventLoop正是如此處理:

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

那么,是否需要每次都捕獲Throwable呢?這是最安全的方法,但性能不高,并不提倡。折中的做法是:在最底層代碼捕獲Throwable,其他層級代碼捕獲Exception。
最后回到開始的問題,拋出Error的方法并不是RPC框架的底層代碼,所以不應(yīng)該捕獲Throwable。那么,框架的底層是否處理了Throwable呢,答案是肯定的,和Netty類似,簡單的記錄日志而不進行任何其他處理。所以,再次遇到這種問題時,需要關(guān)注框架級的日志,本例中由于框架日志和普通日志并不在同一路徑,導(dǎo)致忽略查看框架日志。
又回到了原點,貌似不一樣了。。。

附收集到的關(guān)于ExceptionError的一些看法:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,628評論 19 139
  • 原文鏈接:http://www.dropwizard.io/1.2.0/docs/getting-started....
    Lance_Xu閱讀 1,161評論 0 0
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,276評論 6 342
  • 一絲風/一滴雨/一粒種子/還有一拈塵土//小苗/彎彎的/唱著風一樣的歌/啍著雨一樣的曲/露珠在跳蕩//記得你頑皮的...
    冰涼小小手閱讀 363評論 0 0
  • 樓上的人 今晚放的屁 被我一個不漏地 穩(wěn)穩(wěn)接住
    向日葵愛呀愛太陽閱讀 95評論 0 0

友情鏈接更多精彩內(nèi)容