【问题标题】:Connection already closed连接已关闭
【发布时间】:2016-07-18 19:32:27
【问题描述】:

我正在使用 Grails 2.5.3 和 Tomcat7,经过 8 小时的应用部署后,我们的日志开始出现连接已关闭的问题。一个很好的假设是 MySql 在默认等待时间 8 小时后终止连接。

通过文档,我的池似乎已正确配置以保持空闲连接打开,但似乎并非如此。

我的连接池设置可能有什么问题?

dataSource {
  pooled = true
  url = 'jdbc:mysql://******.**********.us-east-1.rds.amazonaws.com/*****'
  driverClassName = 'com.mysql.jdbc.Driver'
  username = '********'
  password = '******************'
  dialect = org.hibernate.dialect.MySQL5InnoDBDialect
  loggingSql = false
  properties {
    jmxEnabled = true
    initialSize = 5
    timeBetweenEvictionRunsMillis = 10000
    minEvictableIdleTimeMillis = 60000
    validationQuery = "SELECT 1"
    initSQL = "SELECT 1"
    validationQueryTimeout = 10
    testOnBorrow = true
    testWhileIdle = true
    testOnReturn = true
    testOnConnect = true
    removeAbandonedTimeout = 300
    maxActive=100 
    maxIdle=10 
    minIdle=1
    maxWait=30000
    maxAge=900000
    removeAbandoned="true"
    jdbcInterceptors="org.apache.tomcat.jdbc.pool.interceptor.StatementCache;"

  }
}

hibernate {
  cache.use_second_level_cache=true
  cache.use_query_cache=true
  cache.region.factory_class = 'org.hibernate.cache.ehcache.EhCacheRegionFactory'
}

另外,我已经确认运行时的数据源是 (org.apache.tomcat.jdbc.pool.DataSource) 的一个实例

更新 1(未修复) 我们认为我们可能已经发现了问题!我们在 http 会话中存储了一个域类,在阅读了一些关于会话工厂如何工作的信息之后,我们相信存储的 http 对象以某种方式绑定到了一个连接。当用户在 8 小时后从 http 会话访问域类时,我们认为 hibernate 存储了对死连接的引用。它现在正在生产中,我们正在监控。

更新 2(已修复) 我们终于找到了问题所在。删除 removeAbandoned 和 removeAbandonedTimeout 解决了我们所有的问题。我们不完全确定为什么这解决了这个问题,因为我们的假设是这两个属性的存在是为了防止发生什么。唯一的想法是我们的​​数据库更积极地管理废弃的连接。已经超过 4 周了,没有任何问题。

【问题讨论】:

标签: mysql grails spring-jdbc


【解决方案1】:

我在完全不同的设置下遇到了这个问题。对付起来真的不好玩。基本上可以归结为:

  1. 当 Java 进行某种“其他”处理时,您的应用程序的某处有一些连接。这是一种非常基本的复制方法:

    Connection con = (get connection from pool); Sleep(330 seconds); con.close();

  2. 代码没有对上面的数据库连接做任何事情,所以tomcat检测为废弃,并在300秒时将其返回到池中。

  3. 您的应用程序的流量足够大,以至于同一连接(在上述代码中打开和放弃)在应用程序的其他地方的代码的不同部分打开。

  4. 要么原始代码达到 330 秒并关闭连接,要么新代码获取连接并完成并关闭它。此时有两个地方使用相同的连接,其中一个已经关闭它。

  5. 使用同一连接的其他代码位置然后尝试使用或关闭同一连接

  6. 连接已关闭。产生上述错误。

建议修复路线:

使用设置logAbandoned="true" 查找连接被放弃的位置。

【讨论】:

    【解决方案2】:

    我们的网址通常看起来很像:

    url = "jdbc:mysql://localhost/db?useUnicode=yes&characterEncoding=UTF-8&autoReconnect=true"
    

    如果您不想遇到这样的问题,请查看编码参数。

    【讨论】:

    • 好的 - 现在正在生产中运行。我还将validationInterval 从30 秒增加到10 秒。到目前为止,生产时间为 12 小时。
    • 没什么。我们正在使用亚马逊 RDS
    • Nothing 表示你没有检查或者db日志是清楚的?
    【解决方案3】:

    (请参阅有关问题的更新 2)

    删除 removeAbandonedremoveAbandonedTimeout 解决了我们所有的问题。有人可能想提供更详细的答案,说明为什么会这样,因为我们并不完全确定。

    【讨论】:

    • 我知道这已经很老了,但是你看到我的回答了吗?