2017-08-11 102 views
1

首次发布问题。谢谢大家!Tomcat 8连接已被放弃

我遇到的问题已经有一段时间了,我们找不到解决方案。总之,使用Java 8,Spring,Hibernate,PostgreSQL,JSF(PrimeFaces这里),Webflow构建应用程序。 有关连接的问题被关闭,但似乎该应用程序仍然使用它,一些逻辑借用它相同的连接,它只是“失蹄”,因此接下来的时间,并抛出异常:

Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement 
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) 

Caused by: java.sql.SQLException: Connection has already been closed. 
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:117) 
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108) 

应用可以为工作几天没有问题,直到发生什么事,它从哪里得到连接放弃警告从tomcat。从那里,只是不会正常工作,许多过程会拿到关闭的连接,将可测量的失败,因为连接已经关闭:

WARNING [Tomcat JDBC Pool Cleaner[1989780873:1502425160484]] org.apache.tomcat.jdbc.pool.ConnectionPool.abandon Connection has been abandoned PooledConnection[[email protected]]:java.lang.Exception 
    at org.apache.tomcat.jdbc.pool.ConnectionPool.getThreadDump(ConnectionPool.java:1093) 

我们的团队花费在这个问题上几个小时,在网络搜索,并咨询了开发人员,一开始,他们都试图调整Tomcat server.xml,但没有取得任何成功。它可能会在未来运行良好,然后在服务器不一致的情况下出现同样的问题,因此唯一要做的就是重新启动它。我们添加的拦截器并没有帮助,只是作为解决问题的一部分。

只有后来我们才能可靠地复制这个问题,而且这个问题本身就很有趣。所以,如果在事务,自定义或常规Java内抛出任何异常(例如,如果存在基于断言的NoResultException或某个MyCustomException),则Tomcat会抛出Abandoned Connection(关闭它); 60秒后(removeAbandonedTimeout)Tomcat将显示警告消息并且连接将被关闭。但是逻辑仍然指向它的问题,以及更多的连接关闭了线路,更多的事情在执行业务逻辑时破坏了。

有server.xml中配置了两个数据源(一个用于业务逻辑,另一个用于职位)如下:

<Resource auth="Container" driverClassName="org.postgresql.Driver" 
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
initialSize="5" 
jdbcInterceptors= 
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; 
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer; 
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx (threshold=10000)" 
logAbandoned="true" 
maxActive="30" 
minEvictableIdleTimeMillis="30000" 
minIdle="5" 
name="jdbc/my-app-db" 
password="" 
removeAbandoned="true" 
removeAbandonedTimeout="60" 
testOnBorrow="true" 
testOnReturn="false" 
testWhileIdle="true" 
timeBetweenEvictionRunsMillis="5000" 
type="javax.sql.DataSource" 
url="jdbc:postgresql://mydb.rds.amazonaws.com:5432/mydb_db" 
username="" 
validationInterval="30000" 
validationQuery="SELECT 1" /> 

<Resource auth="Container" 
driverClassName="org.postgresql.Driver" 
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" 
initialSize="5" 
jdbcInterceptors= 
"org.apache.tomcat.jdbc.pool.interceptor.ConnectionState; 
org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer; 
org.apache.tomcat.jdbc.pool.interceptor.SlowQueryReportJmx (threshold=10000)" 
logAbandoned="true" 
maxActive="30" 
minEvictableIdleTimeMillis="30000" 
minIdle="5" 
name="jdbc/my-app-db-for-jobs" 
password="" 
removeAbandoned="true" 
removeAbandonedTimeout="60" 
testOnBorrow="true" 
testOnReturn="false" 
testWhileIdle="true" 
timeBetweenEvictionRunsMillis="5000" 
type="javax.sql.DataSource" 
url="jdbc:postgresql://mydb.rds.amazonaws.com:5432/myapp_db" 
username="" 
validationInterval="30000" 
validationQuery="SELECT 1" /> 

和XML配置:

<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="persistenceUnitName" value="acme"></property> 
    <property name="jpaVendorAdapter"> 
     <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
      <property name="showSql" value="true" /> 
      <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" /> 
     </bean> 
    </property> 
</bean> 
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="dataSource" ref="dataSource"></property> 
    <property name="entityManagerFactory" ref="entityManagerFactory"></property> 
</bean> 

和pom.xml的一些依赖关系:

<dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-validator</artifactId> 
     <version>5.2.4.Final</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework.webflow</groupId> 
     <artifactId>spring-faces</artifactId> 
     <version>2.4.2.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework.webflow</groupId> 
     <artifactId>spring-webflow</artifactId> 
     <version>2.4.2.RELEASE</version> 
    </dependency> 
    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-context-support</artifactId> 
     <version>4.1.6.RELEASE</version> 
    </dependency> 

    <dependency> 
     <groupId>org.springframework</groupId> 
     <artifactId>spring-web</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-entitymanager</artifactId> 
    </dependency> 
    <dependency> 
     <groupId>org.postgresql</groupId> 
     <artifactId>postgresql</artifactId> 
     <version>42.1.1</version> 
    </dependency> 

这里是一段代码可能会失败,但是rea LLY任何有交易,或任何查询DB可以在封闭的池连接如绊倒:

PromoCode promoCodeEntity = null; 
    try { 
     JpaTransactionManager transactionManager = (JpaTransactionManager) ApplicationContextProvider 
       .getApplicationContext().getBean("transactionManager"); 
     TransactionDefinition def = new DefaultTransactionDefinition(); 
     TransactionStatus status = transactionManager.getTransaction(def); 

     String jpql = "select p from PromoCode p where p.code = :code"; 
     Query query = em.createQuery(jpql).setParameter("code", promoCode); 
     promoCodeEntity = (PromoCode) query.getSingleResult(); 

     if (promoCodeEntity.getQuantyOfUses() >= promoCodeEntity.getTotalOfUses()) { 
      MyCustomException myCustomException = new MyCustomException("This promo code is not valid: [" + promoCode + "]"); 
      MyCustomException.setCode("PROMO_CODE"); 
      throw myCustomException; 
     } 

     if (!promoCodeEntity.getActive() || promoCodeEntity.getFinish()) { 
      MyCustomException myCustomException = new MyCustomException("This promo code is not valid: [" + promoCode + "]"); 
      myCustomException.setCode("PROMO_CODE"); 
      throw myCustomException; 
     } 

     if (!(promoCodeEntity.getStartDateValid().before(new Date()) 
       && promoCodeEntity.getEndDateValid().after(new Date()))) { 
      MyCustomException myCustomException = new MyCustomException("This promo code is expired: [" + promoCode + "]"); 
      myCustomException.setCode("PROMO_CODE"); 
      throw myCustomException; 
     } 
     promoCodeEntity.setQuantyOfUses(promoCodeEntity.getQuantyOfUses() + 1); 
     transactionManager.commit(status); 
    } catch (NoResultException e) { 
     MyCustomException myCustomException = new MyCustomException("Promo code not found: [" + promoCode + "]"); 
     myCustomException.setCode("PROMO_CODE"); 
     throw myCustomException; 
    } 

有没有人遇到这样的问题,或者我应该在哪里看得更远?我们做了一些驱动程序更新(例如postgres jdbc),目前也正在评估c3p0 Pool,以查看这是否与Tomcat Pool错误相关。但真的很高兴能够理解错误。

回答

0

您需要使用connectionTimeout加入您的dataSources属性。现在你正在使用默认的超时时间,超时后会迫使会话关闭。所以你可以尝试下面的代码添加到dataSources属性:

 connectionTimeout="300000" 
0

试着改变你的属性C3P0。 unreturnedConnectionTimeout处理挂起的连接。如果你有一些查询需要花费太多时间,连接可能会超时,挂起db连接。你的连接有限,所以如果这个过程重复,可能会耗尽你的连接。 testConnectionOnCheckout检查连接是否关闭,如果不关闭则关闭。

c3p0.unreturnedConnectionTimeout=2000 
c3p0.testConnectionOnCheckout=true 
+0

感谢响应。这个问题与查询花费太长时间无关,可能,但现在不行。我们目前使用HikariCP Pool进行了设置,但问题并没有消失。连接可能已用尽,但由于查询超时原因而不同。 – user3711719