首页 文章

SpringMVC,c3p0,hibernate,JPA应用程序泄漏连接导致Too Many Connections错误

提问于
浏览
0

我已经google搜索stackoverflow解决我的问题超过4个小时了,阅读并试图了解发生了什么,但我没有遇到与我的问题有关的解决方案所以道歉如果这听起来像重复,我我会尽力解释究竟发生了什么,这样我就可以深入了解c3p0的内部工作原理 .

我有一个在Tomcat上运行的SpringMVC Web应用程序,Hibernate,JPA和C3P0作为我的池资源 . 在页面重新加载时,应用程序会立即调用数据库以获取随机图像并将其显示在新页面上 . 应用程序运行正常大约30页左右的页面重新加载,但总是崩溃,我必须重新启动mysql,以便应用程序再次工作,它给出以下错误:

2015年4月4日下午12:21:55 com.mchange.v2.resourcepool.BasicResourcePool $ AcquireTask运行警告:com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@634d8e3d - 获取尝试失败!!!清除待处理的收购 . 在尝试获取所需的新资源时,我们未能成功超过允许的最大获取尝试次数(30) . 上次获取尝试异常:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:数据源拒绝 Build 连接,来自服务器的消息:sun.reflect上的sun.reflect.GeneratedConstructorAccessor101.newInstance(Unknown Source)中的“连接太多” . DelegationConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)位于com.mysql.jdbc的com.mysql.jdbc.Util.handleNewInstance(Util.java:411)的java.lang.reflect.Constructor.newInstance(Constructor.java:526)中com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)中的.Util.getInstance(Util.java:386)位于com.mysql的com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) . 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)at com.mysql.jdbc.ConnectionImpl.createNewIO(Co nnectionImpl.java:2311)com.mysql.jdbc.ConnectionImpl . (ConnectionImpl.java:834)at com.mysql.jdbc.JDBC4Connection . (JDBC4Connection.java:47)at sun.reflect.GeneratedConstructorAccessor43.newInstance(Unknown Source)在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 at 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)at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171)at com.mchange.v2.c3p0.impl .C3P0PooledConnectionPool $ 1PooledConnectionResourceP oolManager.acquireResource(C3P0PooledConnectionPool.java:137)at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014)at com.mchange.v2.resourcepool.BasicResourcePool.access $ 800(BasicResourcePool.java:32)at at com.mchange.v2.resourcepool.BasicResourcePool $ AcquireTask.run(BasicResourcePool.java:1810)at 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但它对数据库中 Build 的连接数没有影响 .

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日志:

Apr 05,2015 10:57:30 PM org.hibernate.service.jdbc.connections.internal.ConnectionProviderInitiator instantiateExplicitConnectionProvider INFO:HHH000130:实例化显式连接提供程序:org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider Apr 05,2015 10:57 :30 PM com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource getPoolManager INFO:初始化c3p0池... 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 - > z8kfs x98137ghpr10fde73 | 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]

3 回答

  • 1

    因此,从您遇到的任何事情来看,单个池耗尽的常见症状(应用程序冻结) . 相反,您的应用程序会打开比maxPoolSize更多的Connections,然后在达到服务器端连接限制时失败 . 除非你的服务器东西~20个连接太多,否则你可能会创建多个池 . 设置 wait_timeout 隐藏了问题,因为放弃的DataSources的连接自动关闭()编辑,但它赢得了't be a good solution. If you are making new DataSources for each client, you' ll显着减慢而不是加速你的应用程序,如果那些数据源不是关闭()编辑(似乎他们不是't, or you' d不累积打开连接),你将创建线程和内存泄漏 .

    所以 .

    首先,你是如何记录的?请确保在INFO中记录 com.mchange.* 类,并检查池启动消息 . 在DataSource初始化时,c3p0在INFO上转储大型池配置消息 . 确保您至少看到其中一条消息 . 你看过很多次吗?那就是问题所在 .

    如果我是对的并且您正在打开然后放弃多个c3p0数据源,那么下一个问题就变成了原因 . 应用程序中的池化数据源嵌入在 EntityManagerFactory 对象中,在应用程序的生命周期中应该只有一个对象 . Spring使事情变得简单,但它隐藏了如何/何时构造,销毁等等的细节 . 我认为你可能必须回答的关键问题是为什么Spring是否正在创建多个EntityManagerFactory实例(或者重新创建c3p0 DataSource多个单个EntityManagerFactory中的时间) .

    附: C3P0不提供“loginTimeout”配置参数 .

  • 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.
           }
    

    在我看来,如果你重构你的代码如下所示,问题将得到解决 .

  • 0

    我找到了解决问题的方法,但我不太确定这是最好的方法 .

    我通过my.cnf将wait_timeout设置为5秒:

    [mysqld]
    wait_timeout=5
    

    这似乎可以有效地破坏entityManager正在创建的休眠进程,并允许我长时间运行应用程序而无需重新启动mysql .

    我现在的问题是,这真的是处理由c3p0 EntityManager创建的连接的最佳和唯一方法吗?为什么c3p0不会破坏连接或自行删除它们?这是有意的吗?任何澄清或讨论都表示赞赏 . 谢谢大家:)

相关问题