5

据我了解,“testOnBorrow”和“validationQuery”参数就在我的小巷里,但它们似乎没有按预期工作。

我启动应用程序,运行一些查询,一切顺利。然后我重新启动 postgres 服务器 - 不重新启动 tomcat - 以测试 DataSource 可以处理重新连接,我得到的只是:

    This connection has been closed.; nested exception is org.postgresql.util.PSQLException: This connection has been closed.
    at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:104)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
        at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
        at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:603)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666)
        at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:674)
    at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:729)

...

Caused by: org.postgresql.util.PSQLException: This connection has been closed.
    at org.postgresql.jdbc2.AbstractJdbc2Connection.checkClosed(AbstractJdbc2Connection.java:822)
    at org.postgresql.jdbc3.AbstractJdbc3Connection.prepareStatement(AbstractJdbc3Connection.java:273)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.prepareStatement(AbstractJdbc2Connection.java:301)
    at sun.reflect.GeneratedMethodAccessor38.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor.invoke(AbstractCreateStatementInterceptor.java:67)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.interceptor.ConnectionState.invoke(ConnectionState.java:153)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.TrapException.invoke(TrapException.java:41)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:63)
    at $Proxy35.prepareStatement(Unknown Source)
    at org.springframework.jdbc.core.JdbcTemplate$SimplePreparedStatementCreator.createPreparedStatement(JdbcTemplate.java:1436)

我在用:

  • 春天 3.1
  • PostgreSQL 9.2.1
  • 连接池:org.apache.tomcat.jdbc.pool 7.0.25

我的 Spring bean 配置如下:

public DataSource dataSource() {
    org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
    // from properties file
    dataSource.setDriverClassName(environment
            .getProperty("datasource.driver"));
    dataSource.setUrl(environment.getProperty("datasource.url"));
    dataSource.setUsername(environment.getProperty("datasource.username"));
    dataSource.setPassword(environment.getProperty("datasource.password"));
    // other configurations
    dataSource.setInitialSize(10);
    dataSource.setMinIdle(10);
    dataSource.setMaxIdle(100);
    dataSource.setMaxActive(100);
    dataSource.setDefaultAutoCommit(true);
    dataSource.setMaxWait(6000);
    dataSource.setJmxEnabled(true);
    dataSource
            .setJdbcInterceptors("....ConnectionState;.....StatementFinalizer");
    dataSource.setRemoveAbandoned(true);
    dataSource.setRemoveAbandonedTimeout(10);
    dataSource.setLogAbandoned(true);
    dataSource.setTestOnBorrow(true);
    dataSource.setTestOnReturn(false);
    dataSource.setTestWhileIdle(false);
    dataSource.setUseEquals(false);
    dataSource.setFairQueue(false);
    dataSource.setTimeBetweenEvictionRunsMillis(30000);
    dataSource.setMinEvictableIdleTimeMillis(30000);
    dataSource.setValidationInterval(1800000);
    dataSource.setValidationQuery("SELECT 1");

    return dataSource;
}

任何想法?

谢谢

4

3 回答 3

1

仅在最初从池中借用连接时才进行连接验证。即使在每次查询之前都进行了检查,在检查和查询之间仍然会有一段时间连接可能会丢失。所有查询都应该有某种形式的异常处理来处理查询失败——通常,处理坏的连接句柄,获取一个新的句柄,然后重试查询(如果可能的话)。

在 JDBC 标准中,有一个“connectionErrorOccurred”的回调方法应该在发生这种情况时被调用,但我对 JDBC 和 Java 的工作原理还不够熟悉,不知道如何使用它(或者即使它涵盖了这个)案子)。

在任何情况下,您可以知道连接是好是坏的唯一时间是您尝试使用它并且没有连接池可以自动克服该问题而不使用自定义调用来执行和重试查询,这会中断JDBC“契约”到标准接口。

于 2012-12-13T22:03:06.657 回答
1

我最近遇到了同样的问题,想在网络掉线时自动重新连接 Tomcat JDBC 连接池。和你一样,我也发现 testOnBorrow 和 validationInterval 不适合我。

我最终在 JdbcTemplate 上创建了一个 Around Advice(我们使用 Spring 的 org.springframework.jdbc.core.JdbcTemplate)并在 Advice 内部进行重试。

Spring 配置如下所示:

<bean id="jdbcRetryOnFailureAdvice" class="my.jdbc.adapter.JdbcRetryOnFailureAdvice">
    <property name="maxRetriesOnConnDrop" value="5"/>
    <property name="retryWaitInMillis" value="5000"/>
    <property name="sqlStatesToRetry" value="08003 08001 57P01"/>
</bean>

<aop:config proxy-target-class="true">
    <aop:aspect id="jdbcRetryOnUpdate" ref="jdbcRetryOnFailureAdvice">
        <aop:pointcut id="retryUpdatePointCut"
                      expression="execution(* 
                      org.springframework.jdbc.core.JdbcTemplate.update(..))"/>
        <aop:around   pointcut-ref="retryUpdatePointCut"
                      method="retryOnFailure"/>
    </aop:aspect>
</aop:config>

my.jdbc.adapter.JdbcRetryOnFailureAdvice 类如下所示:

public class JdbcRetryOnFailureAdvice {

    private int maxRetriesOnConnDrop = 0;

    private long retryWaitInMillis = 1000;

    public Object retryOnFailure(ProceedingJoinPoint jp) throws Throwable {
        Object result = null;
        int retryCount = 0;
        do {
            try {
                Object[] args = jp.getArgs();
                if ( args != null && args.length > 0 )
                    result = jp.proceed(jp.getArgs());
                else
                    result = jp.proceed();
                break;
            } catch (DataAccessResourceFailureException ex) {
                if ( retryCount < maxRetriesOnConnDrop ) {
                    LOG.warn("Retrying...(retryCount=" + retryCount + ")");
                    sleep(retryWaitInMillis);
                } else {
                    throw ex;
                }
            }
            retryCount++;
        } while ( retryCount <= maxRetriesOnConnDrop );
        return result;
    }
}
于 2013-07-24T01:22:45.787 回答
1

我有类似的问题。我使用 Postgres 9.4。Spring 4 和 Tomcat 8。我用 Spring 配置解决了问题

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
    <property name="initialSize" value="10" />
    <property name="maxActive" value="25" />
    <property name="maxIdle" value="20" />
    <property name="minIdle" value="10" />
    <property name="driverClassName" value="org.postgresql.Driver" />
    <property name="url" value="${database.url}" /> 
    <property name="username" value="${database.username}" />
    <property name="password" value="${database.password}" />   
    <property name="testOnBorrow" value="true" />
    <property name="validationQuery" value="SELECT 1" />
 </bean>

我已经测试过了。它运作良好!我关闭了 Postgres,所以我有这样的日志:

Caused by: java.net.ConnectException: Connection refused: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)

但是当我启动 postgres 时,一切都很好!为了重新连接到数据库,这两行做了所有事情:

<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
于 2015-09-07T11:22:58.457 回答