Spring Boot集成Druid出現(xiàn)異常報(bào)錯(cuò)的原因及解決
在Spring Boot集成Druid項(xiàng)目中,發(fā)現(xiàn)錯(cuò)誤日志中頻繁的出現(xiàn)如下錯(cuò)誤信息:
discard long time none received connection. , jdbcUrl : jdbc:mysql://******?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8, version : 1.2.3, lastPacketReceivedIdleMillis : 172675
經(jīng)過排查發(fā)現(xiàn)是Druid版本導(dǎo)致的異常,在1.2.2及以前版本并未出現(xiàn)如此異常。而在其以上版本均存在此問題,下面就來分析一下異常原因及解決方案。
異常分析首先上面的異常并不影響程序的正常運(yùn)行,但作為程序員看到程序中不停的出現(xiàn)異常還是難以忍受的。所以還是要刨根問底的解決一下的。
跟蹤堆棧信息會發(fā)現(xiàn)對應(yīng)的異常是從com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal方法中拋出的,對應(yīng)的代碼如下:
if (valid && isMySql) { // unexcepted branch long lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn); if (lastPacketReceivedTimeMs > 0) {long mysqlIdleMillis = currentTimeMillis - lastPacketReceivedTimeMs;if (lastPacketReceivedTimeMs > 0 //&& mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { discardConnection(holder); String errorMsg = 'discard long time none received connection. ' + ', jdbcUrl : ' + jdbcUrl + ', jdbcUrl : ' + jdbcUrl + ', lastPacketReceivedIdleMillis : ' + mysqlIdleMillis; LOG.error(errorMsg); return false;} }}
上述代碼中,MySqlUtils.getLastPacketReceivedTimeMs(conn) 是獲取上一次使用的時(shí)間,mysqlIdleMillis 就是計(jì)算出來空閑的時(shí)間,timeBetweenEvictionRunsMillis 是常量60秒。如果連接空閑了60秒以上,那就discardConnection(holder) 丟棄這個(gè)舊連接并順帶打印了一個(gè)日志LOG.warn(errorMsg)。
原理追蹤在上述代碼中,我們看到進(jìn)入該業(yè)務(wù)邏輯是有前提條件的,也就是valid和isMySql變量同時(shí)為true。isMySql為true是必須的,我們使用的本身就是Mysql數(shù)據(jù)庫。那么是否可以讓valid為false呢?這樣不就不會進(jìn)入該業(yè)務(wù)處理了嗎?
來看看valid的來源,還是在該方法的上面:
boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
我們找到validConnectionChecker的Mysql實(shí)現(xiàn)子類MySqlValidConnectionChecker,該類中對isValidConnection的實(shí)現(xiàn)如下:
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception { if (conn.isClosed()) {return false; } if (usePingMethod) {if (conn instanceof DruidPooledConnection) { conn = ((DruidPooledConnection) conn).getConnection();}if (conn instanceof ConnectionProxy) { conn = ((ConnectionProxy) conn).getRawObject();}if (clazz.isAssignableFrom(conn.getClass())) { if (validationQueryTimeout <= 0) {validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT; } try {ping.invoke(conn, true, validationQueryTimeout * 1000); } catch (InvocationTargetException e) {Throwable cause = e.getCause();if (cause instanceof SQLException) { throw (SQLException) cause;}throw e; } return true;} } String query = validateQuery; if (validateQuery == null || validateQuery.isEmpty()) {query = DEFAULT_VALIDATION_QUERY; } Statement stmt = null; ResultSet rs = null; try {stmt = conn.createStatement();if (validationQueryTimeout > 0) { stmt.setQueryTimeout(validationQueryTimeout);}rs = stmt.executeQuery(query);return true; } finally {JdbcUtils.close(rs);JdbcUtils.close(stmt); }}
我們可以看到上述方法中有三個(gè)返回的地方:第一個(gè)連接已關(guān)閉;第二個(gè)使用ping的形式進(jìn)行檢查;第三,使用select 1的方式進(jìn)行檢查。而使用ping的形式檢查時(shí),無論是否拋異常都會返回true。這里我們禁用該模式即可。
進(jìn)入ping的業(yè)務(wù)邏輯主要靠變量usePingMethod來判斷,追蹤代碼會發(fā)現(xiàn)在這里進(jìn)行的設(shè)置:
public void configFromProperties(Properties properties) { String property = properties.getProperty('druid.mysql.usePingMethod'); if ('true'.equals(property)) {setUsePingMethod(true); } else if ('false'.equals(property)) {setUsePingMethod(false); }}
那么,也就是說,當(dāng)我們把系統(tǒng)屬性druid.mysql.usePingMethod設(shè)置為false即可禁用該功能。
禁用Ping Method找到了問題的根源,那么剩下的就是如何禁用了,通常有三種形式。
第一,在啟動(dòng)程序時(shí)在運(yùn)行參數(shù)中增加:-Ddruid.mysql.usePingMethod=false。
第二,在Spring Boot項(xiàng)目中,可在啟動(dòng)類中添加如下靜態(tài)代碼快:
static { System.setProperty('druid.mysql.usePingMethod','false');}
第三,類文件配置。在項(xiàng)目的DruidConfig類中新增加:
/** 解決druid 日志報(bào)錯(cuò):discard long time none received connection:xxx* */@PostConstructpublic void setProperties(){ System.setProperty('druid.mysql.usePingMethod','false');}
至此,已可以成功關(guān)閉該功能,異常信息再也不會出現(xiàn)了。
為什么要清空空閑60秒以上的連接猜測,阿里給數(shù)據(jù)庫設(shè)置的數(shù)據(jù)庫空閑等待時(shí)間是60秒,mysql數(shù)據(jù)庫到了空閑等待時(shí)間將關(guān)閉空閑的連接,以提升數(shù)據(jù)庫服務(wù)器的處理能力。
MySQL的默認(rèn)空閑等待時(shí)間是8小時(shí),就是「wait_timeout」的配置值。如果數(shù)據(jù)庫主動(dòng)關(guān)閉了空閑的連接,而連接池并不知道,還在使用這個(gè)連接,就會產(chǎn)生異常。
以上就是Spring Boot集成Druid出現(xiàn)異常報(bào)錯(cuò)的原因及解決的詳細(xì)內(nèi)容,更多關(guān)于Spring Boot集成Druid出現(xiàn)異常的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. ASP動(dòng)態(tài)網(wǎng)頁制作技術(shù)經(jīng)驗(yàn)分享2. jsp文件下載功能實(shí)現(xiàn)代碼3. asp.net core項(xiàng)目授權(quán)流程詳解4. 在JSP中使用formatNumber控制要顯示的小數(shù)位數(shù)方法5. CSS3實(shí)現(xiàn)動(dòng)態(tài)翻牌效果 仿百度貼吧3D翻牌一次動(dòng)畫特效6. XMLHTTP資料7. ASP常用日期格式化函數(shù) FormatDate()8. html中的form不提交(排除)某些input 原創(chuàng)9. CSS3中Transition屬性詳解以及示例分享10. ASP基礎(chǔ)入門第八篇(ASP內(nèi)建對象Application和Session)
