您好,登錄后才能下訂單哦!
這篇文章主要介紹Spring Boot集成Druid出現(xiàn)異常報(bào)錯(cuò)的原因是什么,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
在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)過(guò)排查發(fā)現(xiàn)是Druid版本導(dǎo)致的異常,在1.2.2及以前版本并未出現(xiàn)如此異常。而在其以上版本均存在此問(wèn)題,下面就來(lái)分析一下異常原因及解決方案。
首先上面的異常并不影響程序的正常運(yùn)行,但作為程序員看到程序中不停的出現(xiàn)異常還是難以忍受的。所以還是要刨根問(wèn)底的解決一下的。
跟蹤堆棧信息會(huì)發(fā)現(xiàn)對(duì)應(yīng)的異常是從com.alibaba.druid.pool.DruidAbstractDataSource#testConnectionInternal方法中拋出的,對(duì)應(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ì)算出來(lái)空閑的時(shí)間,timeBetweenEvictionRunsMillis 是常量60秒。如果連接空閑了60秒以上,那就discardConnection(holder) 丟棄這個(gè)舊連接并順帶打印了一個(gè)日志LOG.warn(errorMsg)。
在上述代碼中,我們看到進(jìn)入該業(yè)務(wù)邏輯是有前提條件的,也就是valid和isMySql變量同時(shí)為true。isMySql為true是必須的,我們使用的本身就是Mysql數(shù)據(jù)庫(kù)。那么是否可以讓valid為false呢?這樣不就不會(huì)進(jìn)入該業(yè)務(wù)處理了嗎?
來(lái)看看valid的來(lái)源,還是在該方法的上面:
boolean valid = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);
我們找到validConnectionChecker的Mysql實(shí)現(xiàn)子類(lèi)MySqlValidConnectionChecker,該類(lèi)中對(duì)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í),無(wú)論是否拋異常都會(huì)返回true。這里我們禁用該模式即可。
進(jìn)入ping的業(yè)務(wù)邏輯主要靠變量usePingMethod來(lái)判斷,追蹤代碼會(huì)發(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); } }
那么,也就是說(shuō),當(dāng)我們把系統(tǒng)屬性druid.mysql.usePingMethod設(shè)置為false即可禁用該功能。
找到了問(wèn)題的根源,那么剩下的就是如何禁用了,通常有三種形式。
第一,在啟動(dòng)程序時(shí)在運(yùn)行參數(shù)中增加:-Ddruid.mysql.usePingMethod=false。
第二,在Spring Boot項(xiàng)目中,可在啟動(dòng)類(lèi)中添加如下靜態(tài)代碼快:
static { System.setProperty("druid.mysql.usePingMethod","false"); }
第三,類(lèi)文件配置。在項(xiàng)目的DruidConfig類(lèi)中新增加:
/* * 解決druid 日志報(bào)錯(cuò):discard long time none received connection:xxx * */ @PostConstruct public void setProperties(){ System.setProperty("druid.mysql.usePingMethod","false"); }
至此,已可以成功關(guān)閉該功能,異常信息再也不會(huì)出現(xiàn)了。
猜測(cè),阿里給數(shù)據(jù)庫(kù)設(shè)置的數(shù)據(jù)庫(kù)空閑等待時(shí)間是60秒,mysql數(shù)據(jù)庫(kù)到了空閑等待時(shí)間將關(guān)閉空閑的連接,以提升數(shù)據(jù)庫(kù)服務(wù)器的處理能力。
MySQL的默認(rèn)空閑等待時(shí)間是8小時(shí),就是「wait_timeout」的配置值。如果數(shù)據(jù)庫(kù)主動(dòng)關(guān)閉了空閑的連接,而連接池并不知道,還在使用這個(gè)連接,就會(huì)產(chǎn)生異常。
springboot一種全新的編程規(guī)范,其設(shè)計(jì)目的是用來(lái)簡(jiǎn)化新Spring應(yīng)用的初始搭建以及開(kāi)發(fā)過(guò)程,SpringBoot也是一個(gè)服務(wù)于框架的框架,服務(wù)范圍是簡(jiǎn)化配置文件。
以上是“Spring Boot集成Druid出現(xiàn)異常報(bào)錯(cuò)的原因是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。