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