[alibaba/druid]使用ojdbc8驱动连接数据库,密码错误时CreateConnectionThread无法退出

2025-11-10 895 views
7
项目依赖
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>18.15.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>
测试代码
    Thread(
        Runnable {
            var druidDataSource: DruidDataSource? = null
            val connectBO = ConnectBO("jdbc:oracle:thin:@10.10.xx.xx:1521/rac", "username", "errorpassword")
            try {
                druidDataSource = createDataSource(connectBO) as DruidDataSource
                druidDataSource.getConnection(2_000).use {
                    println(it)
                }
            } catch (e: Exception) {
                println(String.format("test connect error, url:[%s]", connectBO.url))
            } finally {
                druidDataSource?.close()
            }
        }
    ).start()
    Thread.sleep(100_000)
}

fun createDataSource(connectBO: ConnectBO): DataSource {
    val props = Properties().apply {
        this[DruidDataSourceFactory.PROP_DRIVERCLASSNAME] = oracle.jdbc.OracleDriver::class.qualifiedName!!
        this[DruidDataSourceFactory.PROP_USERNAME] = connectBO.username
        this[DruidDataSourceFactory.PROP_PASSWORD] = connectBO.password
        this[DruidDataSourceFactory.PROP_URL] = connectBO.url
    }
    props.setProperty(DruidDataSourceFactory.PROP_INITIALSIZE, "1")
    return DruidDataSourceFactory.createDataSource(props)
}

data class ConnectBO(val url: String, val username: String, val password: String)
问题现象

连接池中的CreateConnectionThread未正常关闭,持续创建连接并打印报错日志:

十一月 10, 2021 11:16:18 上午 com.alibaba.druid.pool.DruidDataSource error
严重: create connection SQLException, url: jdbc:oracle:thin:@10.10.xx.xx:1521/rac, errorCode 1017, state 72000
java.sql.SQLException: ORA-01017: invalid username/password; logon denied

    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:494)
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:441)
    at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:436)
    at oracle.jdbc.driver.T4CTTIfun.processError(T4CTTIfun.java:1110)
    at oracle.jdbc.driver.T4CTTIoauthenticate.processError(T4CTTIoauthenticate.java:552)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:540)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:256)
    at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:501)
    at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:1297)
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:694)
    at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:782)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:39)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:704)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1657)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1723)
    at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2801)
原因分析

根据异常堆栈:

2021-10-11 11:39:24,343 ERROR - [com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2803)] create connection SQLException, url: jdbc:oracle:thin:@10.10.60.52:1521/pdb1, errorCode 17002, state 08006
java.sql.SQLRecoverableException: IO 错误: The Network Adapter could not establish the connection
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:774)
    at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:688)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:39)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:691)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1657)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1723)
    at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2801)
Caused by: oracle.net.ns.NetException: The Network Adapter could not establish the connection
    at oracle.net.nt.ConnStrategy.execute(ConnStrategy.java:523)
    at oracle.net.resolver.AddrResolution.resolveAndExecute(AddrResolution.java:521)
    at oracle.net.ns.NSProtocol.establishConnection(NSProtocol.java:660)
    at oracle.net.ns.NSProtocol.connect(NSProtocol.java:286)
    at oracle.jdbc.driver.T4CConnection.connect(T4CConnection.java:1438)
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:518)
    ... 6 more
Caused by: java.io.IOException: Operation interrupted, socket connect lapse 0 ms. /10.10.xx.xx 1521 0 1 true
    at oracle.net.nt.TcpNTAdapter.connect(TcpNTAdapter.java:209)
    at oracle.net.nt.ConnOption.connect(ConnOption.java:161)
    at oracle.net.nt.ConnStrategy.execute(ConnStrategy.java:470)
    ... 11 more
Caused by: java.io.InterruptedIOException: Operation interrupted
    at oracle.net.nt.TimeoutSocketChannel.handleInterrupt(TimeoutSocketChannel.java:311)
    at oracle.net.nt.TimeoutSocketChannel.<init>(TimeoutSocketChannel.java:84)
    at oracle.net.nt.TcpNTAdapter.connect(TcpNTAdapter.java:169)
    ... 13 more

ojdbc8驱动在TimeoutSocketChannel#handleInterrupt方法中清除了线程中断标志,导致Druid的CreateConnectionThread无法在run方法中正常结束。 使用ojdbc6驱动则没有这个问题,Druid能否处理这种问题呢?

回答

6

我现在升级到1.2.7了,还会有这个问题

4

使用MySQL驱动也遇到了CreateConnectionThread无法退出的问题。

驱动依赖:
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.17</version>
        </dependency>
测试代码:
fun main() {
    Thread(
        Runnable {
            var druidDataSource: DruidDataSource? = null
            val connectBO =
                ConnectBO("jdbc:mysql://10.10.xx.xx:3306/test1?autoReconnect=true", "username", "password")
            try {
                druidDataSource = createDataSource(connectBO) as DruidDataSource
                druidDataSource.getConnection(2_000).use {
                    println(it)
                }
            } catch (e: Exception) {
                System.err.println(String.format("test connect error, url:[%s]", connectBO.url))
            } finally {
                druidDataSource?.close()
            }
        }
    ).start()
    Thread.sleep(100_000)
}

fun createDataSource(connectBO: ConnectBO): DataSource {
    val props = Properties().apply {
        this[DruidDataSourceFactory.PROP_USERNAME] = connectBO.username
        this[DruidDataSourceFactory.PROP_PASSWORD] = connectBO.password
        this[DruidDataSourceFactory.PROP_URL] = connectBO.url
    }
    props.setProperty(DruidDataSourceFactory.PROP_INITIALSIZE, "1")
    return DruidDataSourceFactory.createDataSource(props)
}

data class ConnectBO(val url: String, val username: String, val password: String)

当连接的数据库test1不存在时,调用close方法关闭连接池后,由于com.mysql.cj.jdbc.ConnectionImpl#connectWithRetries()方法的第906行捕捉并忽略了InterruptedException,导致Druid的CreateConnectionThread无法退出。

解决建议

能否在CreateConnectionThread的run方法中使用DruidDataSource的closed变量作为退出标识让CreateConnectionThread正确退出。

4

也遇到了 相同的问题,目前就只能设置 Druid 连接失败之后不再自动重连,Oracle 在里面把中断信号给丢了,或者说发送给 创建连接的线程的中断信号给Oracle给捕获了,从Oracle里面的逻辑上来看,Oracle 有自己的中断任务,如果中断任务不存在,会把中断信号给吞了。也是建议使用连接池的状态来控制创建线程和销毁线程的生命周期,中断信号一旦被其他线程,捕获之后是不可控的。

9

这个问题代码合入了吗?最新版本代码看着没合入: