SpringMVC、c3p0、hibernate、JPA Application leaking Connections leads to Too Many Connections 错误
SpringMVC, c3p0, hibernate, JPA Application leaking Connections leads to Too Many Connections error
我已经在 Whosebug 上搜索并搜索了 4 个多小时来解决我的问题,阅读并尝试了解正在发生的事情,但我还没有找到与我的问题相关的解决方案,如果这听起来很抱歉像重复一样,我会尽力解释到底发生了什么,这样我就可以深入了解 c3p0 的内部工作原理。
我在 Tomcat 上有一个 SpringMVC Web 应用程序 运行,使用 Hibernate、JPA 和 C3P0 作为我的池资源。在页面重新加载时,应用程序会调用数据库以获取随机图像并将其显示在新页面上。该应用程序在大约 30 次左右的页面重新加载后运行良好,但最终总是崩溃,我必须重新启动 mysql 才能使应用程序再次运行,它会出现以下错误:
Apr 04, 2015 12:21:55 PM com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask run
WARNING: com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@634d8e3d -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (30). Last acquisition attempt exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections"
at sun.reflect.GeneratedConstructorAccessor101.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1114)
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2493)
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2526)
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2311)
at com.mysql.jdbc.ConnectionImpl.(ConnectionImpl.java:834)
at com.mysql.jdbc.JDBC4Connection.(JDBC4Connection.java:47)
at sun.reflect.GeneratedConstructorAccessor43.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:416)
at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:347)
at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:135)
at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:182)
at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:137)
at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014)
at com.mchange.v2.resourcepool.BasicResourcePool.access0(BasicResourcePool.java:32)
at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810)
at com.mchange.v2.async.ThreadPerTaskAsynchronousRunner$TaskThread.run(ThreadPerTaskAsynchronousRunner.java:255)
这里有关于 files/configurations 的问题:
spring.xml:
<context:component-scan base-package="com.clathrop.infographyl.dao" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="infographylPU" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/infographyl_db" />
<property name="user" value="user" />
<property name="password" value="passwd" />
<!-- Pool properties -->
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="acquireIncrement" value="1" />
<property name="maxStatements" value="0" />
<property name="idleConnectionTestPeriod" value="3000" />
<property name="loginTimeout" value="300" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
persistence.xml:
<persistence-unit name="infographylPU" transaction-type="RESOURCE_LOCAL">
<class>com.clathrop.infographyl.model.Infographic</class>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>
这是一个基本查询的样子,在这个 class 我有一个 EntityManager 作为实例变量,我在每个查询完成后关闭()entityManager 但它对数量没有影响在数据库中建立的连接。
InfographicDaoImpl.java:
@Repository
public class InfographicDaoImpl implements InfographicDao{
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public void insertInfographic(Infographic infographic){
try{
entityManager.persist(infographic);
} catch (Exception e){
e.printStackTrace();
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> findAllInfographics(){
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
List<Infographic> igList = entityManager.createQuery(cq).getResultList();
return igList;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Infographic getRandomInfographic(Integer tableSize){
Random rand = new Random();
int randomIndex = rand.nextInt((tableSize-1)+1) + 1;
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
cq.where(builder.equal(root.<Integer>get("id"), randomIndex));
Infographic randomIg = entityManager.createQuery(cq).getSingleResult();
return randomIg;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Integer getRowCount(){
try{
Number result = (Number) entityManager.createNativeQuery("Select count(id) from infographics").getSingleResult();
return result.intValue();
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> listInfographics(Integer startIndex, Integer pageSize){
List<Infographic> igList = new ArrayList<Infographic>();
String sStartIndex = Integer.toString(startIndex);
String sPageSize = Integer.toString(pageSize);
try{
List list = entityManager.createNativeQuery("Select * from infographics limit " + sStartIndex + ", " + sPageSize).getResultList();
for(Object ig : list){
igList.add((Infographic) ig);
}
return igList;
} catch(Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
}
此时我对我的应用程序中发生的事情有几个问题。我的理解是 c3p0 正在处理与数据库建立的连接,我认为通过池连接,建立的连接将被重用,但是当我查看 [=] 中的进程列表时,我得到了越来越多的连接列表51=]。连接数不断增长,直到应用程序最终抛出上面显示的连接过多警告。我的问题是为什么 c3p0 不重用连接?我是否在某处缺少配置以告诉它重用预先存在的连接?为什么 entityManager.close() 貌似没有影响?如果我误解了如何使用 hibernate 和 c3p0,请告诉我我遗漏了什么。非常感谢任何帮助,提前致谢。
更新:
SEVERE: The web application [/infographyl] appears to have started a thread named [com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0] but has failed to stop it. This is very likely to create a memory leak.
我已将问题缩小为 tomcat 启动并部署有问题的应用程序时识别到的内存泄漏。我现在的问题是试图找出导致问题的可能配置(或缺少配置)。
我目前的解决方法是设置 my.cnf 中的 wait_timeout 以杀死超过 5 秒的 processes/connections,这目前看来还可以,但这是不可持续的解决方案,我想知道关闭连接的正确方法。
更新 2:
com.mchange.* 信息日志:
Apr 05, 2015 10:57:30 PM org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider
INFO: HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider
Apr 05, 2015 10:57:30 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager
INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kfsx98137ghpr10fde73|6aa74262, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx98137ghpr10fde73|6aa74262, idleConnectionTestPeriod -> 3000, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/infographyl_db, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
我认为,某些数据库操作会导致异常。在这种情况下,会抛出 Exception 并且 EntityManager 不会按预期关闭。通常你的数据库操作应该看起来像
EntityManager em=getEntityManagerSomehow();
try{
do your stuff with db here
}catch(Exception ex){
handle exception here eg. log or rethrow.
}finally{
em.close(); // always close entity manager even if exception occures.
}
在我看来,如果您像上图那样重构代码,问题就会得到解决。
我找到了解决我的问题的方法,但我不太确定这是最好的方法。
我通过以下方式将 my.cnf 中的 wait_timeout 设置为 5 秒:
[mysqld]
wait_timeout=5
这似乎可以有效地破坏 entityManager 正在创建的休眠进程,并允许我 运行 应用程序长时间使用而无需重新启动 mysql。
我现在的问题是,这真的是处理由 c3p0 + EntityManager 创建的连接的最好且唯一的方法吗?为什么 c3p0 不破坏连接或自行删除它们?这是故意的吗?任何澄清或讨论表示赞赏。谢谢大家:)
因此,从您所描述的一切来看,您的应用程序似乎正在创建然后放弃多个 c3p0 池。您不会遇到单个池耗尽(应用程序冻结)的常见症状。相反,您的应用程序打开的连接数多于 maxPoolSize,然后在达到服务器端连接限制时失败。除非您的服务器东西 ~20 连接太多,否则您可能会创建多个池。设置 wait_timeout
隐藏了问题,因为被放弃的数据源的连接会自动关闭(),但这不是一个好的解决方案。如果您为每个客户端制作新的数据源,您将大大减慢而不是加速您的应用程序,并且如果这些数据源没有关闭()ed(看起来它们不是,或者您不会累积打开的连接) ,您将造成线程和内存泄漏。
所以。
首先,您如何记录事情?请确保 com.mchange.*
类 已记录在 INFO 中,并检查查找池启动消息。 c3p0 在 DataSource 初始化时在 INFO 转储一个大型池配置消息。确保您至少看到其中一条消息。你见过他们很多次吗?那就是问题所在
如果我是对的,并且您正在打开然后放弃多个 c3p0 数据源,那么下一个问题就是为什么。应用程序中的池数据源嵌入在一个 EntityManagerFactory
对象中,在应用程序的生命周期中应该只有一个。 Spring 使事情看起来简单,但它隐藏了 how/when 事物构造、破坏等的细节。我认为您可能需要回答的关键问题是 why/whether Spring正在创建多个 EntityManagerFactory 实例(或在单个 EntityManagerFactory 中多次重新创建 c3p0 DataSource)。
p.s。 c3p0 不提供 "loginTimeout" 配置参数。
我已经在 Whosebug 上搜索并搜索了 4 个多小时来解决我的问题,阅读并尝试了解正在发生的事情,但我还没有找到与我的问题相关的解决方案,如果这听起来很抱歉像重复一样,我会尽力解释到底发生了什么,这样我就可以深入了解 c3p0 的内部工作原理。
我在 Tomcat 上有一个 SpringMVC Web 应用程序 运行,使用 Hibernate、JPA 和 C3P0 作为我的池资源。在页面重新加载时,应用程序会调用数据库以获取随机图像并将其显示在新页面上。该应用程序在大约 30 次左右的页面重新加载后运行良好,但最终总是崩溃,我必须重新启动 mysql 才能使应用程序再次运行,它会出现以下错误:
Apr 04, 2015 12:21:55 PM com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask run WARNING: com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@634d8e3d -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (30). Last acquisition attempt exception: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection, message from server: "Too many connections" at sun.reflect.GeneratedConstructorAccessor101.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.Util.getInstance(Util.java:386) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1114) at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2493) at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2526) at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2311) at com.mysql.jdbc.ConnectionImpl.(ConnectionImpl.java:834) at com.mysql.jdbc.JDBC4Connection.(JDBC4Connection.java:47) at sun.reflect.GeneratedConstructorAccessor43.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:416) at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:347) at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:135) at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:182) at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPoolPooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:137) at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014) at com.mchange.v2.resourcepool.BasicResourcePool.access0(BasicResourcePool.java:32) at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810) at com.mchange.v2.async.ThreadPerTaskAsynchronousRunner$TaskThread.run(ThreadPerTaskAsynchronousRunner.java:255)
这里有关于 files/configurations 的问题:
spring.xml:
<context:component-scan base-package="com.clathrop.infographyl.dao" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="infographylPU" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- Connection properties -->
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/infographyl_db" />
<property name="user" value="user" />
<property name="password" value="passwd" />
<!-- Pool properties -->
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="acquireIncrement" value="1" />
<property name="maxStatements" value="0" />
<property name="idleConnectionTestPeriod" value="3000" />
<property name="loginTimeout" value="300" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
persistence.xml:
<persistence-unit name="infographylPU" transaction-type="RESOURCE_LOCAL">
<class>com.clathrop.infographyl.model.Infographic</class>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>
这是一个基本查询的样子,在这个 class 我有一个 EntityManager 作为实例变量,我在每个查询完成后关闭()entityManager 但它对数量没有影响在数据库中建立的连接。
InfographicDaoImpl.java:
@Repository
public class InfographicDaoImpl implements InfographicDao{
@PersistenceContext
private EntityManager entityManager;
@Override
@Transactional
public void insertInfographic(Infographic infographic){
try{
entityManager.persist(infographic);
} catch (Exception e){
e.printStackTrace();
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> findAllInfographics(){
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
List<Infographic> igList = entityManager.createQuery(cq).getResultList();
return igList;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Infographic getRandomInfographic(Integer tableSize){
Random rand = new Random();
int randomIndex = rand.nextInt((tableSize-1)+1) + 1;
try{
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Infographic> cq = builder.createQuery(Infographic.class);
Root<Infographic> root = cq.from(Infographic.class);
cq.select(root);
cq.where(builder.equal(root.<Integer>get("id"), randomIndex));
Infographic randomIg = entityManager.createQuery(cq).getSingleResult();
return randomIg;
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public Integer getRowCount(){
try{
Number result = (Number) entityManager.createNativeQuery("Select count(id) from infographics").getSingleResult();
return result.intValue();
} catch (Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
@Override
public List<Infographic> listInfographics(Integer startIndex, Integer pageSize){
List<Infographic> igList = new ArrayList<Infographic>();
String sStartIndex = Integer.toString(startIndex);
String sPageSize = Integer.toString(pageSize);
try{
List list = entityManager.createNativeQuery("Select * from infographics limit " + sStartIndex + ", " + sPageSize).getResultList();
for(Object ig : list){
igList.add((Infographic) ig);
}
return igList;
} catch(Exception e){
e.printStackTrace();
return null;
} finally {
entityManager.close();
}
}
}
此时我对我的应用程序中发生的事情有几个问题。我的理解是 c3p0 正在处理与数据库建立的连接,我认为通过池连接,建立的连接将被重用,但是当我查看 [=] 中的进程列表时,我得到了越来越多的连接列表51=]。连接数不断增长,直到应用程序最终抛出上面显示的连接过多警告。我的问题是为什么 c3p0 不重用连接?我是否在某处缺少配置以告诉它重用预先存在的连接?为什么 entityManager.close() 貌似没有影响?如果我误解了如何使用 hibernate 和 c3p0,请告诉我我遗漏了什么。非常感谢任何帮助,提前致谢。
更新:
SEVERE: The web application [/infographyl] appears to have started a thread named [com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread-#0] but has failed to stop it. This is very likely to create a memory leak.
我已将问题缩小为 tomcat 启动并部署有问题的应用程序时识别到的内存泄漏。我现在的问题是试图找出导致问题的可能配置(或缺少配置)。
我目前的解决方法是设置 my.cnf 中的 wait_timeout 以杀死超过 5 秒的 processes/connections,这目前看来还可以,但这是不可持续的解决方案,我想知道关闭连接的正确方法。
更新 2: com.mchange.* 信息日志:
Apr 05, 2015 10:57:30 PM org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider INFO: HHH000130: Instantiating explicit connection provider: org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider Apr 05, 2015 10:57:30 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager INFO: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> z8kfsx98137ghpr10fde73|6aa74262, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> z8kfsx98137ghpr10fde73|6aa74262, idleConnectionTestPeriod -> 3000, initialPoolSize -> 3, jdbcUrl -> jdbc:mysql://localhost:3306/infographyl_db, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 20, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 5, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
我认为,某些数据库操作会导致异常。在这种情况下,会抛出 Exception 并且 EntityManager 不会按预期关闭。通常你的数据库操作应该看起来像
EntityManager em=getEntityManagerSomehow();
try{
do your stuff with db here
}catch(Exception ex){
handle exception here eg. log or rethrow.
}finally{
em.close(); // always close entity manager even if exception occures.
}
在我看来,如果您像上图那样重构代码,问题就会得到解决。
我找到了解决我的问题的方法,但我不太确定这是最好的方法。
我通过以下方式将 my.cnf 中的 wait_timeout 设置为 5 秒:
[mysqld]
wait_timeout=5
这似乎可以有效地破坏 entityManager 正在创建的休眠进程,并允许我 运行 应用程序长时间使用而无需重新启动 mysql。
我现在的问题是,这真的是处理由 c3p0 + EntityManager 创建的连接的最好且唯一的方法吗?为什么 c3p0 不破坏连接或自行删除它们?这是故意的吗?任何澄清或讨论表示赞赏。谢谢大家:)
因此,从您所描述的一切来看,您的应用程序似乎正在创建然后放弃多个 c3p0 池。您不会遇到单个池耗尽(应用程序冻结)的常见症状。相反,您的应用程序打开的连接数多于 maxPoolSize,然后在达到服务器端连接限制时失败。除非您的服务器东西 ~20 连接太多,否则您可能会创建多个池。设置 wait_timeout
隐藏了问题,因为被放弃的数据源的连接会自动关闭(),但这不是一个好的解决方案。如果您为每个客户端制作新的数据源,您将大大减慢而不是加速您的应用程序,并且如果这些数据源没有关闭()ed(看起来它们不是,或者您不会累积打开的连接) ,您将造成线程和内存泄漏。
所以。
首先,您如何记录事情?请确保 com.mchange.*
类 已记录在 INFO 中,并检查查找池启动消息。 c3p0 在 DataSource 初始化时在 INFO 转储一个大型池配置消息。确保您至少看到其中一条消息。你见过他们很多次吗?那就是问题所在
如果我是对的,并且您正在打开然后放弃多个 c3p0 数据源,那么下一个问题就是为什么。应用程序中的池数据源嵌入在一个 EntityManagerFactory
对象中,在应用程序的生命周期中应该只有一个。 Spring 使事情看起来简单,但它隐藏了 how/when 事物构造、破坏等的细节。我认为您可能需要回答的关键问题是 why/whether Spring正在创建多个 EntityManagerFactory 实例(或在单个 EntityManagerFactory 中多次重新创建 c3p0 DataSource)。
p.s。 c3p0 不提供 "loginTimeout" 配置参数。