我已经用Google搜索并搜索了stackoverflow,以便解决我的问题超过4个小时,阅读并尝试了解正在发生的事情,但我没有遇到与我的问题相关的解决方案,所以如果这听起来很道歉就像重复一样,我会尽力解释究竟发生了什么,这样我就可以深入了解c3p0的内部工作原理。
我有一个在Tomcat上运行的SpringMVC Web应用程序,Hibernate,JPA和C3P0作为我的池资源。在页面重新加载时,应用程序会立即调用数据库以获取随机图像并将其显示在新页面上。应用程序运行正常,大约30页左右的页面重新加载但总是崩溃,我必须重新启动mysql才能让应用程序再次运行,它会出现以下错误:
2015年4月4日下午12:21:55 com.mchange.v2.resourcepool.BasicResourcePool $ AcquireTask run 警告:com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@634d8e3d - 获取尝试失败!!!清除待处理的收购。在尝试获取所需的新资源时,我们未能成功超过允许的最大获取尝试次数(30)。上次获取尝试异常: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:数据源拒绝建立连接,来自服务器的消息:"连接太多" at sun.reflect.GeneratedConstructorAccessor101.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:526) 在com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 在com.mysql.jdbc.Util.getInstance(Util.java:386) 在com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015) 在com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) 在com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) 在com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1114) 在com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2493) 在com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2526) 在com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2311) 在com.mysql.jdbc.ConnectionImpl。(ConnectionImpl.java:834) 在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) 在com.mysql.jdbc.Util.handleNewInstance(Util.java:411) 在com.mysql.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:416) 在com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:347) 在com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:135) 在com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:182) 在com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool $ 1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:137) 在com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014) 在com.mchange.v2.resourcepool.BasicResourcePool.access $ 800(BasicResourcePool.java:32) 在com.mchange.v2.resourcepool.BasicResourcePool $ AcquireTask.run(BasicResourcePool.java:1810) 在com.mchange.v2.async.ThreadPerTaskAsynchronousRunner $ TaskThread.run(ThreadPerTaskAsynchronousRunner.java:255)
以下是提供问题背景的相关文件/配置:
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>
这是基本查询的样子,在这个类中我有一个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处理与数据库的连接,我认为通过池连接,所做的连接将被重用,但是当我查看mysql中的进程列表时,我得到了越来越多的连接列表。连接数一直在增长,直到应用程序最终抛出上面显示的太多连接警告。我的问题是为什么c3p0不重用连接?我错过了某个地方的配置,告诉它重用已有的连接吗?为什么entityManager.close()似乎没有影响?如果我误解了如何使用hibernate和c3p0,请让我知道我错过了什么。任何帮助都非常感谢,提前谢谢。
更新:
严重:Web应用程序[/ infographyl]似乎已经启动了一个名为[com.mchange.v2.async.ThreadPoolAsynchronousRunner $ PoolThread-#0]的线程,但未能阻止它。这很可能会造成内存泄漏。
我已将问题缩小到内存泄漏,当tomcat启动并部署有问题的应用程序时,内存泄漏正在被识别。我现在的问题是试图找出导致问题的可能配置(或缺少配置)。
我目前的解决方法是在my.cnf中设置wait_timeout来杀死超过5秒的进程/连接,这似乎现在还可以,但它不是一个可持续的解决方案,我想知道关闭我的联系的正确方法。
UPDATE2: com.mchange。* INFO日志:
2015年4月5日下午10:57:30 org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider 信息:HHH000130:实例化显式连接提供程序:org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider 2015年4月5日下午10:57:30 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager 信息:初始化c3p0池... com.mchange.v2.c3p0.ComboPooledDataSource [acquireIncrement - &gt; 1,acquireRetryAttempts - &gt; 30,acquireRetryDelay - &gt; 1000,autoCommitOnClose - &gt; false,automaticTestTable - &gt; null,breakAfterAcquireFailure - &gt; false,checkoutTimeout - &gt; 0,connectionCustomizerClassName - &gt; null,connectionTesterClassName - &gt; com.mchange.v2.c3p0.impl.DefaultConnectionTester,dataSourceName - &gt; z8kfsx98137ghpr10fde73 | 6aa74262,debugUnreturnedConnectionStackTraces - &gt; false,description - &gt; null,driverClass - &gt; com.mysql.jdbc.Driver,factoryClassLocation - &gt; null,forceIgnoreUnresolvedTransactions - &gt; false,identityToken - &gt; z8kfsx98137ghpr10fde73 | 6aa74262,idleConnectionTestPeriod - &gt; 3000,initialPoolSize - &gt; 3,jdbcUrl - &gt; jdbc:mysql:// localhost:3306 / infographyl_db,lastAcquisitionFailureDefaultUser - &gt; null,maxAdministrativeTaskTime - &gt; 0,maxConnectionAge - &gt; 0,maxIdleTime - &gt; 0,maxIdleTimeExcessConnections - &gt; 0,maxPoolSize - &gt; 20,maxStatements - &gt; 0,maxStatementsPerConnection - &gt; 0,minPoolSize - &gt; 5,numHelperThreads - &gt; 3,numThreadsAwaitingCheckoutDefaultUser - &gt; 0,preferredTestQuery - &gt; null,properties - &gt; {user = ******,password = ******},propertyCycle - &gt; 0,testConnectionOnCheckin - &gt; false,testConnectionOnCheckout - &gt; false,unreturnedConnectionTimeout - &gt; 0,usesTraditionalReflectiveProxies - &gt;错误的]
答案 0 :(得分:1)
因此,从您描述的所有内容来看,听起来您的应用程序正在创建,然后放弃多个c3p0池。您没有遇到单个池耗尽的常见症状(应用程序冻结)。相反,您的应用程序会打开比maxPoolSize更多的Connections,然后在达到服务器端连接限制时失败。除非你的服务器东西~20个连接太多,否则你可能会创建多个池。设置wait_timeout
会隐藏问题,因为放弃的DataSources的连接会自动关闭()编辑,但这不是一个好的解决方案。如果您为每个客户端创建新的DataSource,您将大大减慢而不是加速您的应用程序,如果这些DataSource不是close()ed(它们似乎不是,或者您不会累积打开的Connections) ,你将创建线程和内存泄漏。
所以
首先,你是如何记录的?请确保在INFO中记录com.mchange.*
个类,并检查查找池启动消息。在DataSource初始化时,c3p0在INFO上转储大型池配置消息。确保您至少看到其中一条消息。你看过很多次吗?那就是问题所在。
如果我是对的并且您正在打开然后放弃多个c3p0数据源,那么下一个问题就变成了原因。应用程序中的池化数据源嵌入在EntityManagerFactory
对象中,在应用程序的生命周期中应该只有一个对象。 Spring使事情变得简单,但它隐藏了如何/何时构造,销毁等等的细节。我认为你可能必须回答的关键问题是为什么Spring是否创建多个EntityManagerFactory实例(或者重新创建c3p0 DataSource多个单个EntityManagerFactory中的时间。)
P.S。 c3p0没有提供“loginTimeout”配置参数。
答案 1 :(得分:0)
我认为,某些数据库操作会导致异常。在这种情况下抛出异常并且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.
}
在我看来,如果您重构代码如下所示,问题将得到解决。
答案 2 :(得分:0)
我找到了解决问题的方法,但我不太确定这是最好的方法。
我通过以下方式将my.cnf中的wait_timeout设置为5秒:
[mysqld]
wait_timeout=5
这似乎可以有效地破坏entityManager正在创建的休眠进程,并允许我长时间运行应用程序,而无需重新启动mysql。
我现在的问题是,这真的是处理由c3p0 + EntityManager创建的连接的最佳和唯一方法吗?为什么没有c3p0破坏连接或自行删除它们?这是有意的吗?任何澄清或讨论都表示赞赏。谢谢大家:))