記一次因數(shù)據(jù)庫重啟導(dǎo)致Druid連接池GetConnectionTimeoutException,active 0,無法獲取連接的問題

問題描述:
當(dāng)線上有任務(wù)正在運(yùn)行并且需要獲取數(shù)據(jù)庫連接的時(shí)候,此時(shí)重啟了數(shù)據(jù)庫(線上是hive),導(dǎo)致線上服務(wù)后續(xù)一直無法獲取連接,一直報(bào)GetConnectionTimeoutException異常,從而后續(xù)跑的任務(wù)全部失敗。但是如果在沒有任務(wù)正在運(yùn)行時(shí)重啟數(shù)據(jù)庫,在數(shù)據(jù)庫重啟之后,開始運(yùn)行任務(wù),可以正常獲取連接,任務(wù)都是正常執(zhí)行。
所報(bào)異常:

Caused by: com.alibaba.druid.pool.GetConnectionTimeoutException: wait millis 60000, active 0, maxActive 100, creating 0, createErrorCount 6 
at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1732) 
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1404) 
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1384) 
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1374) 
at io.transwarp.backup.utils.DataSourceUtil.getHiveConn(DataSourceUtil.java:94) 
at io.transwarp.backup.utils.TableInfoUtil.getHiveConnAndSetPool(TableInfoUtil.java:583) 
at io.transwarp.TableSyncTaskV2.init(TableSyncTaskV2.java:119) 
... 13 more 
Caused by: java.sql.SQLException: java.sql.SQLException: Could not open connection to jdbc:hive2://172.26.4.22:10000/default: java.net.ConnectException: 拒絕連接 (Connection refused) 
at org.apache.hive.jdbc.HiveConnection$1.run(HiveConnection.java:296) 
Caused by: java.sql.SQLException: Could not open connection to jdbc:hive2://172.26.4.22:10000/default: java.net.ConnectException: 拒絕連接 (Connection refused) 
at org.apache.hive.jdbc.HiveConnection.openTransport(HiveConnection.java:398) 
at org.apache.hive.jdbc.HiveConnection.access$100(HiveConnection.java:60) 
at org.apache.hive.jdbc.HiveConnection$1.run(HiveConnection.java:285) 
Caused by: org.apache.thrift.transport.TTransportException: java.net.ConnectException: 拒絕連接 (Connection refused) 
at org.apache.thrift.transport.TSocket.open(TSocket.java:187) 
at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java:266) 
at org.apache.thrift.transport.TSaslClientTransport.open(TSaslClientTransport.java:37) 
at org.apache.hive.jdbc.HiveConnection.openTransport(HiveConnection.java:395) 
... 2 more 
Caused by: java.net.ConnectException: 拒絕連接 (Connection refused) 
at java.net.PlainSocketImpl.socketConnect(Native Method) 
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) 
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) 
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) 
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) 
at java.net.Socket.connect(Socket.java:589) 
at org.apache.thrift.transport.TSocket.open(TSocket.java:182) 
... 5 more 

奇怪現(xiàn)象:可以看到active connection是0,但是就是一直無法獲取數(shù)據(jù)庫連接,而數(shù)據(jù)庫服務(wù)因?yàn)橹貑⒑笠彩钦5?,最終經(jīng)過排查發(fā)現(xiàn)是因?yàn)樵O(shè)置了 breakAfterAcquireFailure 參數(shù)為true,如果為false,當(dāng)獲取不到連接的時(shí)候,Druid將會(huì)無限的打印獲取不到連接的異常信息,所以當(dāng)時(shí)將該參數(shù)設(shè)置為了false。當(dāng)該參數(shù)為true的時(shí)候,當(dāng)多次重試之后無法獲取連接,DruidDataSource中的CreateConnectionThread線程就會(huì)因?yàn)?代碼中的break 而跳出自己的for循環(huán)獲取連接的機(jī)制而導(dǎo)致生命終結(jié),通過jstack可以看到里面是沒有該線程的信息的,所以該線程是已經(jīng)死掉了,通過日志排查CreateConnectionThread確實(shí)是有終結(jié)前的日志信息。因?yàn)閯?chuàng)建連接的線程已經(jīng)死掉了,那后續(xù)肯定是無法創(chuàng)建出新的連接的。
但是我們又不能將breakAfterAcquireFailure設(shè)置為false,因?yàn)檫@將在無法獲取連接的時(shí)候無限的錯(cuò)誤日志產(chǎn)生。自認(rèn)為這個(gè)是Druid連接池本身存在的不完善地方,自己所使用的連接池版本是druid-1.1.24.jar,已經(jīng)是目前1.1.x中最新的版本,查看1.2.x版本這部分代碼邏輯跟1.1.x基本是一致的。
目前自己的解決方案是自己寫一個(gè)對連接池 CreateConnectionThread 的存活檢測方法,每次獲取連接的時(shí)候先去檢測下該線程的狀態(tài),如果該線程已經(jīng)死掉,則直接close連接池,然后重新創(chuàng)建,算是一種hack的方式,但至少能work解決線上的問題。

CreateConnectionThread 的存活檢測機(jī)制:

  public static boolean checkDataSourceFailContinuousStatus(DruidDataSource dataSource) {
    if (!dataSource.isBreakAfterAcquireFailure()) {
      log.info("this data source does not open `BreakAfterAcquireFailure`");
      return false;
    }
    boolean failContinuous = dataSource.isFailContinuous(); // fail over retry attempts
    boolean createConnectionThreadIsAlive = true;
    try {
      CreateConnectionThread createConnectionThread = getCreateConnectionThread(dataSource);
      createConnectionThreadIsAlive = createConnectionThread.isAlive();
    } catch (Exception e) {
      log.error("Get dataSource: " + dataSource + "'s createConnectionThread fails.");
    }
    return failContinuous && !createConnectionThreadIsAlive;
  }

  public static CreateConnectionThread getCreateConnectionThread(DruidDataSource dataSource)
      throws NoSuchFieldException, IllegalAccessException {
    Field createConnectionThread = dataSource.getClass().getDeclaredField("createConnectionThread");
    createConnectionThread.setAccessible(true);
    CreateConnectionThread createConnectionThreadInstance = (CreateConnectionThread) createConnectionThread
        .get(dataSource);
    log.info("Get dataSource: {}'s createConnectionThread: {}", dataSource,
        createConnectionThreadInstance);
    return createConnectionThreadInstance;
  }

之前遇到該問題也是一頭霧水,參考以下文章博客內(nèi)容找到問題所在:


image.png

可能自己并沒有深入研究該問題是否可以通過其他參數(shù)設(shè)置來解決,歡迎有好的解決方式的朋友指正。

參考博客地址:
https://github.com/alibaba/druid/issues/2130
http://www.itdecent.cn/p/33ec6897218a

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容