2

在对我的 Web 应用程序进行压力测试时, Hibernate似乎无法再创建与数据库的连接。

Web 应用程序是使用Spring 3.0、Hibernate 3.6 和 c3p0 0.9.2.1 连接池开发的。它在Tomcat 7 下运行。DBMS 是 MySQL Server 5.5。所有事务都由 Spring 通过@Transactional注解管理。

以下是Hibernate/c3p0的一些设置

    <property name="acquireIncrement" value="5" />
    <property name="initialPoolSize" value="15" />
    <property name="minPoolSize" value="10" />
    <property name="maxPoolSize" value="75" />
    <property name="maxStatements" value="100" /> 
    <property name="maxIdleTime" value="600" />   
    <property name="checkoutTimeout" value="2500" /> 
    <property name="autoCommitOnClose" value="true" />
    <bean id="transactionManager"
      class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />      
    <property name="defaultTimeout" value="15" />
    </bean>

这是 JDBC 连接 URL:

jdbc.url=jdbc:mysql://myserver:myport/mydb?connectTimeout=31000&socketTimeout=30000

当我达到大约 200 个并发用户时,CPU 负载接近 100%(但连接池似乎仍低于 75),并出现以下异常:

 SEVERE: Cannot connect to database.
 org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransa
 ctionException: Could not open Hibernate Session for transaction; nested exception is org.hibernate.exception.GenericJDBCException: Cannot open connec
 tion
         at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
         at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
         at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
         at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
         at com.my-company.SimpleConnetionLogFilter.doFilter(SimpleConnetionLogFilter.java:132)
         at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
         at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
         at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
         at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
         at org.apache.coyote.ajp.AjpProcessor.process(AjpProcessor.java:200)
         at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
         at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
         at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
         at java.lang.Thread.run(Unknown Source)
 Caused by: org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is org
 .hibernate.exception.GenericJDBCException: Cannot open connection
         at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:596)
         at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:371)
         at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
         at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:105)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:622)
         at com.my-company.bizlogic.spring.XService$$EnhancerByCGLIB$$4e6a04f7.digest(<generated>)
         at com.my-company.XController.handleRequest(XController.java:55)
         at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:48)
         at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
         at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
         at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
         ... 24 more
 Caused by: org.hibernate.exception.GenericJDBCException: Cannot open connection
         at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:140)
         at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:128)
         at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
         at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:52)
         at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:449)
         at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
         at org.hibernate.jdbc.JDBCContext.connection(JDBCContext.java:160)
         at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:81)
         at org.springframework.orm.hibernate3.HibernateTransactionManager.doBegin(HibernateTransactionManager.java:551)
         ... 35 more
 Caused by: java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
         at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
         at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
         at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:687)
         at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
         at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:81)
         at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
         ... 39 more
 Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@7624a28d -- timeout at awaitAvailable()
         at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1416)
         at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:606)
         at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:526)
         at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:755)
         at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:682)
         ... 42 more

我显然不明白的是,当池中的所有 75 个连接都被使用时会发生什么?

我认为下一笔交易将等待一个免费连接。

由于我已经为交易设置了defaultTimeout,我希望在这种情况下会发生这种情况。这种错误将在应用程序中进行管理,客户端将知道服务器正忙。但目前生成了一个通用的“服务器内部错误”,因为在 Tomcat 方面,MySQL 似乎不再可用,而不仅仅是忙。

此外,我是否在设置中遗漏了什么?我已经尝试提出一些 Hibernate/c3p0 和 JDBC 参数,但唯一的影响是我得到了一个不同的异常,总是与 JDBC 连接有关。

4

2 回答 2

3

我们在对我们的应用程序 (NON JTA) 进行简单的负载测试时发现了一个类似的问题。我们更改了连接释放模式,这解决了我们的问题。希望它对你有用:

休眠属性 hibernate.connection.release_mode=after_transaction

于 2013-04-09T13:28:48.043 回答
1

您设置了 c3p0 参数 checkoutTimeout ,并且您正在通过连接泄漏耗尽池,或者您只是非常努力地推动它们,因此某些客户端无法在 checkoutTimeout 期间获取连接。

处理方式:

1)确保您没有泄漏连接。尝试设置 unreturnedConnectionTimeout 和 debugUnreturnedConnectionStackTraces(见这里

2)如果没有连接泄漏,请更好地配置您的 DataSource 以便它可以处理您的负载。首先,尝试增加numHelperThreads,使其显着大于您正在运行的内核数量(因为这些线程通常与数据库在 IO 上保持一致)。接下来,尝试更大的 maxPoolSize。您还可以重新配置语句池。为简单起见,我会关闭它,直到您解决此问题(将 maxStatements 设置为零)。当连接池运行良好时,您可以将其重新打开,但 100 条语句很少能处理 200 条左右的连接,您将无效率地使用 PreparedStatements。考虑使用maxStatementsPerConnection而不是 maxStatements。将其值设置为应用程序客户端经常使用的 PreparedStatements 的数量。

3) 如果您的 DataSource 仍然无法及时处理负载,请接受更长的checkoutTimeout(或将 checkoutTimeout 设置为零,以便客户端永远不会超时)。

不相关...您确定要将autoCommitOnClose设置为 true 吗?

祝你好运!

于 2013-04-09T12:00:31.307 回答