線下Tomcat出現故障,查看日志報錯:
最后調查的問題是:
某個Service中有個方法使用 httpclient 調用了一個外部接口,而當時這個外部接口服務出現故障,導致httpclient調用卡死了,一直沒有響應,因為在Service方法中,也就是在數據庫事務中,httpclient調用卡死導致 數據庫連接一直無法釋放,卡死的httpclient調用不斷的增大,數據庫連接不斷的被占用並且無法釋放,最終達到了最大值:
<!-- 連接池中允許的最大連接數。缺省值:10;推薦的公式:((core_count * 2) + effective_spindle_count) -->
<property name="maximumPoolSize" value="15" />
其他功能需要訪問數據庫時,就報錯:HikariPool-1 - Connection is not available, request timed out after 30000ms
這里問題的關鍵在於 不能在事務中調用外部接口,不能在事務中有耗時的操作等存在。這樣會導致事務變成大事務,長時間占用數據庫連接、有可能還導致事務長時間持有相關鎖從而導致死鎖。
最佳的解決辦法,自然是把 httpclient 外部調用移到事務外面。實時證明,移到事務外面就恢復正常了。
另外,httpclient 調用應該加上超時現在,比如超過2秒沒有返回,就斷開:
URL url = new URL(urlAddress); URLConnection conn = url.openConnection(); conn.setConnectTimeout(2000); // 2秒沒有連上,則超時 conn.setReadTimeout(2000); // 2秒沒有響應,則超時
這樣也可以緩解問題。但是最好的方法還是把外部調用移到事務外面!
這里有個原理需要弄清楚:
service方法里面就是一個 httpclient 調用,沒有查詢數據庫,為什么還是 會占用數據庫連接呢????
原因是 數據庫事務肯定是需要基於數據庫連接的!就行我們在mysql客戶端里面執行:begin transaction 命令一樣,先得連上mysql 。沒有連上mysql,無法開啟事務。
這也是為什么在 service方法里面沒有查詢mysql,但是還是會占用一個數據庫連接的原因所在。