org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes 正在阻塞线程

org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes is blocking threads

我是 运行 一个 tomcat 服务器,其服务是使用 spring v 4.1.0 开发的。我正在创建 jdbc 到 informix 数据库的连接,偶尔会出错。这些连接是单一连接而不是合并(因为我根据不同的输入标准连接到动态生成的数据库主机)。

随着时间的推移,一切似乎都进展顺利,然后突然之间,我开始获得 tomcat 线程的大幅上升,这种情况一直持续到我达到最大线程数并且对服务器的所有请求都被拒绝。执行线程转储显示所有线程都挂在 org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes。

- org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(javax.sql.DataSource) @bci=56, line=204 (Interpreted frame)
- org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(javax.sql.DataSource) @bci=5, line=134 (Interpreted frame)
- org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(javax.sql.DataSource) @bci=6, line=97 (Interpreted frame)
- org.springframework.jdbc.support.JdbcAccessor.getExceptionTranslator() @bci=22, line=99 (Interpreted frame)
- org.springframework.jdbc.support.JdbcAccessor.afterPropertiesSet() @bci=25, line=138 (Interpreted frame)
- org.springframework.jdbc.core.JdbcTemplate.<init>(javax.sql.DataSource, boolean) @bci=50, line=182 (Interpreted frame)
- com.business.stores.data.dao.impl.BaseDAOImpl.getJdbcTemplate(int) @bci=86, line=53 (Interpreted frame)
...

我已经提取了上面列出的 spring class 的源代码并且其中有一个同步块,但我不确定为什么它会无法执行并挂起启动系统中的所有线程。 (似乎在它被阻塞之后,任何后续的 SQL 错误也将被阻塞,直到盒子上没有可用的线程为止。这是来自 Spring 的代码:

public SQLErrorCodes getErrorCodes(DataSource dataSource) {
    Assert.notNull(dataSource, "DataSource must not be null");
    if (logger.isDebugEnabled()) {
        logger.debug("Looking up default SQLErrorCodes for DataSource [" + dataSource + "]");
    }

    synchronized (this.dataSourceCache) {
        // Let's avoid looking up database product info if we can.
        SQLErrorCodes sec = this.dataSourceCache.get(dataSource);
        if (sec != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("SQLErrorCodes found in cache for DataSource [" +
                        dataSource.getClass().getName() + '@' + Integer.toHexString(dataSource.hashCode()) + "]");
            }
            return sec;
        }
        // We could not find it - got to look it up.
        try {
            String dbName = (String) JdbcUtils.extractDatabaseMetaData(dataSource, "getDatabaseProductName");
            if (dbName != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Database product name cached for DataSource [" +
                            dataSource.getClass().getName() + '@' + Integer.toHexString(dataSource.hashCode()) +
                            "]: name is '" + dbName + "'");
                }
                sec = getErrorCodes(dbName);
                this.dataSourceCache.put(dataSource, sec);
                return sec;
            }
        }
        catch (MetaDataAccessException ex) {
            logger.warn("Error while extracting database product name - falling back to empty error codes", ex);
        }
    }

    // Fallback is to return an empty SQLErrorCodes instance.
    return new SQLErrorCodes();
}

--------更新 在这一点上,我无法确定是什么锁定了 dataSourceCache 或如何修复它。

为 spring 模块打开日志记录(和调试),然后通过在不同环境(因此密码不同)的站点调用该服务来强制解决问题。该服务按预期返回无效密码响应,但日志中有这些行。

似乎已正确加载数据:

2015-10-27 21:09:26,677||DEBUG||SQLErrorCodesFactory.getErrorCodes(175)||||SQL error codes for 'Informix Dynamic Server' found

但它在检索数据时遇到了一些问题:

2015-10-27 21:09:33,162||DEBUG||SQLErrorCodesFactory.getErrorCodes(199)||||Looking up default SQLErrorCodes for DataSource [org.springframework.jdbc.datasource.SingleConnectionDataSource@149e2931]
2015-10-27 21:09:34,254||DEBUG||SQLErrorCodesFactory.getErrorCodes(217)||||Database product name cached for DataSource [org.springframework.jdbc.datasource.SingleConnectionDataSource@50e91794]: name is 'Informix Dynamic Server'

2015-10-27 21:09:34,255||INFO ||MarkdownVoidByCashierDAOImpl.getVoidByCashierFromStore(47)||||Created JDBC Template for 68

然后它抛出了我预期的错误:

2015-10-27 21:09:34,317||WARN ||SQLErrorCodesFactory.getErrorCodes(227)||||Error while extracting database product name - falling back to empty error codes
org.springframework.jdbc.support.MetaDataAccessException: Could not get Connection for extracting meta data; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Incorrect password or user com.informix.asf.IfxASFRemoteException: user1@::ffff:10.63.112.131 is not known on the database server.
        at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:297)
        at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:324)
        at org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(SQLErrorCodesFactory.java:214)
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(SQLErrorCodeSQLExceptionTranslator.java:134)
        at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(SQLErrorCodeSQLExceptionTranslator.java:97)
        at org.springframework.jdbc.support.JdbcAccessor.getExceptionTranslator(JdbcAccessor.java:99)
        at org.springframework.jdbc.support.JdbcAccessor.afterPropertiesSet(JdbcAccessor.java:138)
        at org.springframework.jdbc.core.JdbcTemplate.<init>(JdbcTemplate.java:182)
...

当然,这似乎也没有重现问题(我真的没想到,之前重现问题的尝试都失败了)所以我会继续监控直到问题再次出现。

------更新 2

所以盒子上的问题又出现了。尽管通过调试查看日志,但我没有看到太多指向根本原因的信息。

我一遍又一遍地看到这个基本模式:

2015-10-27 21:28:11,178||DEBUG||SQLErrorCodesFactory.getErrorCodes(199)||||Looking up default SQLErrorCodes for DataSource [org.springframework.jdbc.datasource.SingleConnectionDataSource@3da15c49]
...
2015-10-27 21:28:13,481||DEBUG||SQLErrorCodesFactory.getErrorCodes(217)||||Database product name cached for DataSource [org.springframework.jdbc.datasource.SingleConnectionDataSource@207e4667]: name is 'Informix Dynamic Server'
2015-10-27 21:28:13,482||DEBUG||SQLErrorCodesFactory.getErrorCodes(175)||||SQL error codes for 'Informix Dynamic Server' found

唯一改变的是单连接数据源末尾的十六进制值。

关于一两个错误,我看到以下内容:

2015-10-27 21:27:33,622||WARN ||SQLErrorCodesFactory.getErrorCodes(227)||||Error while extracting database product name - falling back to empty error codes

但我相信只有在我将完全无效的服务器名称作为目标时才会出现。不过,它似乎确实在每次 SQL 调用时都进入了同步块。日志中包含 "Looking for" 与 "found" 的行的 grep 显示了大约 300 的差异,其中查找没有找到相应的结果。这与线程阻塞和无法前进是一致的,因为查找调试行发生在同步块之外。

我遇到了同样的问题,但我找到了解决方案。因为jdbc连接或者数据库连接没有超时属性并且默认超时是永不超时,所以在队列或者池满之后,this.dataSourceCache.get(dataSource);需要另一个资源来处理,所以它永远不会超时并且没有space到运行这一行,所以它永远在这里等待。

解决办法是设置超时时间jdbc或者你用什么连接数据库。希望对你有帮助。