炸裂了:Tomcat服務瘋狂GC,CPU1200%

oracle數據庫版本升級12後,線上服務突然每天白天不定時抽風。

1、調用端表現為接口調用超時;

2、Tomcat服務假死,查看Catalina.out日誌緩慢輸出,過幾分鐘應用OutOfMemory異常;

3、top查看linux負載飆高,tomcat進程CPU佔用率超過100%,甚至達到1000%多;

剛開始調整JVM參數,增大持久代大小permsize=2g maxpermsize=4g,運行半天后又掛了;

增加dump輸出,查看dump日誌,由於太大也看不出啥異樣,用MAT分析日誌,工具一下就奔潰了;

查看異常日誌前後,發現接口返回日誌中的json串中,有key為$ref,value為 $xxxx.xxxx的元素,進一步發現json格式化時,會自動檢測是否存在重複引用和循環引用的元素,如果存在,為了避免堆棧溢出,自動停止了遞歸,系統用的是阿里開源的fastjson,阿里還是很強大的,藉此發現了程序裡一些不規範的寫法,進行了修正,但是問題依舊;

奔潰了,數據庫升級前都沒做過應用升級,奇怪啊!

用戶投訴越來越多,組長說重啟主機試試吧。遇到棘手的問題,同事寫了個應急腳本,如果cpu使用率超過90%就自動重啟服務,雖然解決了用戶投訴,但是還有些接口調用得手工處理,不是長久之計。

再想想是不是和數據庫有關呢?查看oracle的awr日誌,看能不能發現什麼端倪。發現部分sql有性能問題,通過建立索引解決一些bug;發現redolog切換頻繁,指標異常logfile switch(checkpoint incomplete),通過聯繫dba,調整logfile大小,程序好幾天不掛了;過了幾天問題突然又跑出來了,也許是這幾天應用比較閒;

重新審視應用,嘗試關閉mybatis日誌輸出:

<setting>

<setting>

調整jdbc參數:去掉參數removeAbandoned,removeAbandonedTimeout

問題依舊啊!!!

在自動重啟腳本中,增加jstack輸出,查看應用剛異常時堆棧問題;

通過幾次異常堆棧的分析,總是有一個接口,但狀態是RUNNING,不應該啊!!

針對該接口,查看接口調用日誌表,查看異常前後,只有請求,沒有返回的接口做了下統計,就是上面的這個接口,哪裡不對?查看請求參數,接口代碼,發現請求參數不按套路來啊, 接口服務端沒有校驗這種流氓請求,導致對數據庫表的全量查詢,這不掛才怪呢!!至此問題原因總算找到了,接下來就很簡單,增加接口限制,這也提醒我們,編寫接口時要規範,避免不確定性,偷懶的下場啊!!!

事情還沒結束,反思一下問題核查過程,歷時3個月左右,難道我們不能提前發現這樣的問題嗎??

回想10年行業經驗,這種線上問題似乎總會遇到。與數據量有關,系統上線時數據量小,不會有性能問題;與接口編碼規範有關,如果接口規範點編寫,就不會有這樣的問題了,還可以通過代碼review機制提前發現;如果沒有團隊內代碼review機制,可以通過在應用中增加檢測層來提前發現應用異常;也可以在問題出現後,增加檢測手段來儘快發現定位問題。避免人力物力的浪費。

如何檢測?

針對dao層,檢測sql運行影響的行數,設定閾值,增加預警。

這方面如果使用了Mybatis的話,打開mybatis日誌,輸出到ELK,或者其他alert工具中,即可有效監控。

下一次,想寫寫關於ELK,敬請期待。

Elasticsearch、Logstash、Kibana


分享到:


相關文章: