使用tomcat池和Spring自动重新连接postgres

时间:2012-12-03 17:39:34

标签: spring postgresql connection-pooling

据我了解,“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)

我正在使用:

  • Spring 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 个答案:

答案 0 :(得分:1)

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

在JDBC标准中,有一个“connectionErrorOccurred”的回调方法,应该在出现这种情况时调用它,但我对JDBC和Java如何工作以了解如何使用它(或者即使它涵盖了这种情况)。

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

答案 1 :(得分: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;
    }
}

答案 2 :(得分:0)

我有类似的问题。我使用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" />

答案 3 :(得分:-1)

谢谢,添加两个属性也解决了我的配置问题。

因此,我在datasource资源的Tomcat上下文文件中添加了该文件, 讨论的两个属性:testOnBorrow="true" validationQuery="SELECT 1"

我正在使用Spring 4.1.5和PostgreSql 9.5

下一个Googler的名字:-)

N.M。