MySQL 连接超时问题 - 使用 Hibernate 和 ORM 的 Tomcat 上的 Grails 应用程序

Posted

技术标签:

【中文标题】MySQL 连接超时问题 - 使用 Hibernate 和 ORM 的 Tomcat 上的 Grails 应用程序【英文标题】:MySQL Connection Timeout Issue - Grails Application on Tomcat using Hibernate and ORM 【发布时间】:2011-02-14 00:30:42 【问题描述】:

我在 VPS 上的 Ubuntu 中的 Tomcat 上运行了一个小型 grails 应用程序。我使用 mysql 作为我的数据存储,除非我离开应用程序超过半天(8 小时?),否则一切正常。我做了一些搜索,显然这是 mysql.cnf 中的默认wait_timeout,因此 8 小时后连接将断开,但 Tomcat 不知道,因此当下一个用户尝试查看该站点时,他们将看到连接失败错误。刷新页面将解决此问题,但我想完全摆脱该错误。对于我的 MySql (5.0.75) 版本,我只有 my.cnf 并且它不包含这样的参数,无论如何更改此参数并不能解决问题。

这个Blog Post 似乎报告了一个类似的错误,但我仍然不完全理解我需要配置什么来修复这个问题,而且我希望有一个比另一个第三方库更简单的解决方案。我正在运行的机器有 256MB 内存,我正在尝试将运行的程序/服务的数量保持在最低限度。

我可以在 Grails / Tomcat / MySql 中配置一些东西来让它消失吗?

提前致谢,

Gav

来自我的 Catalina.out;

2010-04-29 21:26:25,946 [http-8080-2] ERROR util.JDBCExceptionReporter  - The last packet successfully received from the server was 102,906,722 milliseconds$
2010-04-29 21:26:25,994 [http-8080-2] ERROR errors.GrailsExceptionResolver  - Broken pipe
java.net.SocketException: Broken pipe
        at java.net.SocketOutputStream.socketWrite0(Native Method)
         ...
2010-04-29 21:26:26,016 [http-8080-2] ERROR util.JDBCExceptionReporter  - Already closed.
2010-04-29 21:26:26,016 [http-8080-2] ERROR util.JDBCExceptionReporter  - Already closed.
2010-04-29 21:26:26,017 [http-8080-2] ERROR servlet.GrailsDispatcherServlet  - HandlerInterceptor.afterCompletion threw exception
org.hibernate.exception.GenericJDBCException: Cannot release connection
        at java.lang.Thread.run(Thread.java:619)
Caused by: java.sql.SQLException: Already closed.
        at org.apache.commons.dbcp.PoolableConnection.close(PoolableConnection.java:84)
        at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.close(PoolingDataSource.java:181)
        ... 1 more

【问题讨论】:

【参考方案1】:

参考此article,您的 DBCP 连接池中有陈旧的连接被操作系统或防火墙静默丢弃。

解决方案是在应用程序中实际使用连接之前定义一个验证查询并对连接进行完整性检查。 在 grails 中,这实际上是通过修改 grails-app/conf/spring/Resource.groovy 文件并添加以下内容来完成的:

beans = 
  dataSource(BasicDataSource) 
    //run the evictor every 30 minutes and evict any connections older than 30 minutes.
    minEvictableIdleTimeMillis=1800000
    timeBetweenEvictionRunsMillis=1800000
    numTestsPerEvictionRun=3
    //test the connection while its idle, before borrow and return it
    testOnBorrow=true
    testWhileIdle=true
    testOnReturn=true
    validationQuery="SELECT 1"
  
 

【讨论】:

grails-app/conf/spring/Resource.grooy 是否与 grails-app/conf/spring/resources.groovy 相同?我正在使用 grails 1.2.1,当我添加它时,我遇到了这个错误:=== 无法为连接 URL 'null' java.sql.SQLException 创建类 '' 的 JDBC 驱动程序:没有合适的驱动程序 ===跨度> 确实,如果我创建了一个新的 Resource.groovy,它就可以工作。我猜现在还好。 @firnnauriel 这真的符合您在创建 Resource.groovy 文件时的预期吗? @skaz 实际上没有。更好的解决方案是使用 c3p0 库并更新 resources.groovy。 @firnnauriel 感谢您回复我。我在 DataSource.groovy 文件中找到了代码,它似乎(在某种程度上)成功了。【参考方案2】:

在 grails 1.3.X 中,您可以修改 DataSource.groovy 文件中的 evictor 值,以确保在空闲期间使用池连接。这将确保 mysql 服务器不会超时连接。

production 
  dataSource 
    pooled = true
    // Other database parameters..
    properties 
       maxActive = 50
       maxIdle = 25
       minIdle = 5
       initialSize = 5
       minEvictableIdleTimeMillis = 1800000
       timeBetweenEvictionRunsMillis = 1800000
       maxWait = 10000
    

验证此工作的快速方法是修改 MySQL my.cnf 配置文件 [mysql] 元素并添加具有低值的 wait_time 参数。

【讨论】:

【参考方案3】:

尝试通过在 DataSources.groovy 中添加以下内容来增加打开的 MySQL 连接数:

dataSource 
     driverClassName = "com.mysql.jdbc.Driver"
     pooled=true
     maxActive=10
     initialSize=5
     // Remaining connection params

如果您想全力以赴,请尝试实现连接池;这是一个useful link。

【讨论】:

【参考方案4】:

对于 grails 1.3.X,我必须在 Bootstrap.groovy 中添加以下代码:

  def init = servletContext ->
  def ctx=servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)

    //implement test on borrow
    def dataSource = ctx.dataSource
    dataSource.targetDataSource.setMinEvictableIdleTimeMillis(1000 * 60 * 30)
    dataSource.targetDataSource.setTimeBetweenEvictionRunsMillis(1000 * 60 * 30)
    dataSource.targetDataSource.setNumTestsPerEvictionRun(3)
    dataSource.targetDataSource.setTestOnBorrow(true)
    dataSource.targetDataSource.setTestWhileIdle(true)
    dataSource.targetDataSource.setTestOnReturn(false)
    dataSource.targetDataSource.setValidationQuery("SELECT 1")

  

我还必须导入 org.codehaus.groovy.grails.commons.ApplicationAttributes

【讨论】:

【参考方案5】:

将这些参数添加到数据源

        testOnBorrow = true
        testWhileIdle = true
        testOnReturn = true

查看这篇文章了解更多信息 http://sacharya.com/grails-dbcp-stale-connections/

【讨论】:

【参考方案6】:

从grails 2.3.6开始默认配置已经有防止超时关闭连接的选项

这些是新的默认值。

    properties 
       // See http://grails.org/doc/latest/guide/conf.html#dataSource for documentation
       ....
       minIdle = 5
       maxIdle = 25
       maxWait = 10000
       maxAge = 10 * 60000
       timeBetweenEvictionRunsMillis = 5000
       minEvictableIdleTimeMillis = 60000
       validationQuery = "SELECT 1"
       validationQueryTimeout = 3
       validationInterval = 15000
       testOnBorrow = true
       testWhileIdle = true
       testOnReturn = false
       jdbcInterceptors = "ConnectionState;StatementCache(max=200)"
       defaultTransactionIsolation = java.sql.Connection.TRANSACTION_READ_COMMITTED
    

【讨论】:

【参考方案7】:

您的 JDBC 连接字符串是什么样的?您可以在数据源配置中设置autoReconneect 参数,例如

jdbc:mysql://hostname/mydb?autoReconnect=true

【讨论】:

对我不起作用。据我所知,这只在尝试失败后重新建立连接,并且仍然返回异常。

以上是关于MySQL 连接超时问题 - 使用 Hibernate 和 ORM 的 Tomcat 上的 Grails 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

MySQL连接超时自动断开连接应该怎样处理

超时重新连接 MySQL

keepalived下,mysql连接超时

php连接mysql使用一段时间后连接数据库超时

mysql连接超时问题

mysql连接超时后无法从apache DBCP连接池获取连接