本文源自 公-衆-号 IT老哥 的分享
IT老哥,一個在大廠做進階Java開發的程式員,每天分享技術幹貨文章
老哥哔哔叨
大家應該都經曆過
雙十一
吧,那個流量大的
恐怖
吧,那個并發高的
吓人
吧。那麼在一個高并發的系統裡,有哪些點是影響系統性能的呢,今天我們來講其中一個點:
自定義異常
如果對大家有所幫助,請給個【在看】和【點贊】
瘋狂的異常
為什麼異常會影響性能
首先給大家看一段
JDK
的
Throwable
源碼
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
複制
上面這段
JDK
的源碼就是抛出異常時會調用的方法,這段方法暴露出兩個問題
- 使用了
修飾整個異常方法synchronized
- 将異常追蹤資訊放到了堆棧中(想想
和JVM
)線程
異常種類
- 業務異常這些是我們自定義的、可以預知的異常,抛出這種異常并不表示系統出了問題,而是正常業務邏輯上的需要,例如使用者名密碼錯誤、參數錯誤等。
- 系統異常往往是運作時異常,比如資料庫連接配接失敗、IO 失敗、空指針等,這種異常的産生多數表示系統存在問題,需要人工排查定位。
相信大家都接觸過異常,對于業務異常,我們隻需要簡單的知道一個描述問題的字元串即可,棧追蹤資訊對我們的意義并不大。而對于系統異常,追蹤資訊才是排查錯誤不可或缺的參考。
大家試想,如果前端傳的參數錯了,系統裡就抛出一個異常,那麼在雙十一的情況下一秒鐘得抛出多少個異常呢?
問題思考
- 抛異常的時候是不是會被 synchronized 上同步鎖?
- 需不需要線程去執行?
- 是不是得建立異常對象?
- 需不需要堆棧去存儲?
- 需不需要 jvm 去垃圾回收?
性能測試
- 建立普通 Java 對象 (CustomObject extends HashMap)
- 建立普通 Java 異常對象(CustomException extends Exception)
- 建立改進的 Java 業務異常對象 (CustomException extends Exception,覆寫 fillInStackTrace 方法,并且去掉同步)
測試結果
(運作環境:xen 虛拟機,5.5G 記憶體,8 核;jdk1.6.0_18)
(10 個線程,建立 10000000 個對象所需時間)
- 普通 Java 對象:
45284 MS
- 普通 java 異常:
205482 MS
- 改進的 Java 業務異常:
16731 MS
大家可以看到正常抛出 Exception 的和覆寫了 fillInStackTrace 的 Exception,性能差距了很多倍,如果高并發的系統裡,就像雪球一樣越滾越大。影響系統的并發量。
解決方案:覆寫 fillInStackTrace
我們來看看非常 NB 的
kafka源碼
是如何優化的。
/* avoid the expensive and useless stack trace for api exceptions */
/* 翻譯:避免對api異常進行昂貴且無用的堆棧跟蹤 */
@Override
public Throwable fillInStackTrace() {
return this;
}
複制
很多開源架構都是這樣處理,避免不必要的性能浪費。
老哥結語
什麼是匠人精神,就是将一件事情做到極緻。優化永無止境,且行且珍惜。
雲伺服器,雲硬碟,資料庫(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos資源包,消息隊列ckafka,點播資源包,實時音視訊套餐,網站管家(WAF),大禹BGP高防(包含高防包及高防IP),雲解析,SSL證書,手遊安全MTP,移動應用安全、 雲直播等等。