Spring 应用程序在 8 小时后失去与 MySql 的连接。如何正确配置?

Posted

技术标签:

【中文标题】Spring 应用程序在 8 小时后失去与 MySql 的连接。如何正确配置?【英文标题】:Spring app losing connection to MySql after 8 hours. How to properly configure? 【发布时间】:2012-01-22 01:18:36 【问题描述】:

我有一个 Spring 应用程序,我相信它使用 DBCP 连接池连接到 mysql 数据库。我说相信,因为这不是我非常擅长的领域,如果一切设置正确,我并不乐观。我运行应用程序没有问题,一切正常。问题在一夜之间发生。该应用程序没有大量使用,一夜之间它显然失去了与 MySql 的连接。我查看了它,发现 MySql 有一个 8 小时的窗口,然后它断开连接或其他什么。我对此很好,但是当用户在早上尝试登录时,他们会收到如下错误:

通讯链接故障。最后一个数据包在 60,000,000 毫秒前成功接收。最后一个数据包在 15 毫秒前成功设置。

这就是问题所在。我需要他们能够在早上重新连接而不会遇到这个问题。我似乎能够修复它的唯一方法是反弹 Tomcat 服务器。从调查来看,似乎 DBCP 池应该能够以某种方式防止这种情况,但我找不到关于如何配置它的可靠信息来源。我希望这里有人可以为我提供一些见解。这是我当前的配置,全部在 Spring xml 文件中完成:

app-data.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config />
<context:component-scan base-package="com.vz.sts.domain" />
<context:component-scan base-package="com.vz.sts.persistence" />
<context:component-scan base-package="com.vz.sts.service" />

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="database" value="MYSQL" />
            <property name="showSql" value="true" />
        </bean>
    </property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/app" />
    <property name="username" value="root" />
    <property name="password" value="admin" />
    <property name="initialSize" value="5" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="jdbcUserService" class="org.springframework.security.provisioning.JdbcUserDetailsManager">
    <property name="dataSource" ref="dataSource"/>
    <property name="authenticationManager" ref="authenticationManager"/>
</bean>

<bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">
    <property name="userPropertyToUse" value="username" />
</bean>

<tx:annotation-driven />
</beans>

我不确定需要添加哪些特定属性才能让应用重新连接到数据库。我不介意它是否会在几个小时后关闭连接,但它应该会自动重新连接并且不会抛出这样的错误。我什至也不肯定它实际上设置为使用连接池。非常感谢任何帮助,谢谢。

更新

我找到了this page,我认为我需要做的就是添加 ValidationQuery 属性。任何人都可以验证这是否会产生欲望影响,同时将其他所有内容保持默认状态?我相信这将利用 DBCP 的 testOnBorrow 方面。我不完全理解解释说 testOnBorrow 做了什么,但我认为这会做我想要的。有人确认吗?谢谢。

【问题讨论】:

一个想法是有一个简单的脚本来访问服务以使其每隔一小时左右保持“温暖”。如果问题是由于不活动而导致居民服务离线,这将防止这种情况发生。 谢谢,但我知道 DBCP 可以重新连接而不会出现这个问题,或者有一个设置可以防止这种情况发生,这就是我希望解决的方法。 我同意,这就是为什么我将其发布为评论,而不是答案。我的建议只不过是一种变通方法,但它确实会为您的用户消除症状,直到您找到问题的解决方案:) 【参考方案1】:

简短的回答是应该足够了。 DBCP支持从连接池中借用连接测试(默认),也支持返回测试和空闲测试。

了解这里可能出了什么问题也是值得的。听起来您的 Tomcat 服务器和数据库之间的某些东西在超时后断开了空闲连接(例如路由器或防火墙)。这样做的问题是 Tomcat 认为它仍然有一个有效的连接,尝试对连接做一些工作并失败,但保持连接处于活动状态并将其返回到池中。现在,如果从池中获得相同的断开连接,则任何进一步与数据库对话的尝试都将失败。

我认为这是 Michael Nygard 出色的“释放它!”这本书在他的一个故事中描述了这种情况。

您还需要了解 MySQL 如何清理死连接,因为当 Tomcat 在 8 小时后失去连接时,数据库也将不知道失败的连接。

最后一点,如果您使用的是 Tomcat 7,请切换到他们的新 connection pool,因为它提供比 DBCP 更好的性能。

【讨论】:

你是说我应该使用其他测试之一吗?回归还是闲置?我知道它们默认为 false,所以我需要打开它们。我喜欢使用任何最好的解决方案,但正如我所说,我不完全理解 DBCP 页面上的解释说这三种方法的作用。 mysql服务器设置wait_timeout默认为8小时。空闲连接在 8 小时后被删除,JDBC 驱动程序在您对该连接发出查询之前不会检测到这一点,并且您会获得上述堆栈跟踪。 (对于仅在工作时间使用的应用程序,您可以轻松获得 8 小时的空闲时间)。通常设置 DBCP testWhileIdle、validationQuery 和 timeBetweenEvictionRunsMillis 可以帮助解决此类问题,因为它会 ping 连接并使其保持活动状态。验证查询可以设置为“select 1” 好的,非常感谢。并感谢 Select 1 声明。比我用的好多了。我将在下一个工作日查看这些修复程序是否有效。非常感谢。 在一个周末离开并添加 testWhileIdle 和 timeBetweenEvictionRunsMillis 之后,我可以验证这确实解决了问题。非常感谢您的回答和帮助。 @nos 我正在使用 Hikari,我们没有 testWhileIdle 选项??你有什么建议??我使用 SELECT 1 作为连接测试查询..【参考方案2】:

我的朋友,DBCP 做了一个他无法兑现的承诺。呵呵。我发现自己遇到了这个问题,它归结为最近放置在中间的一些新防火墙,它切断了空闲时间超过 X 小时的空闲连接。因此,Db 无法通知我的客户端(及其套接字)conn 正在关闭并且套接字保持打开状态,因此池无法知道 conn 不可用。结果:早上的第一次查询尝试因超时而失败,而第二次按预期工作。即使使用了validationQuery,DBCP 也没有检查一个已经有效的连接(不要问我为什么,我才发现)

解决方案 1?由于它是一个生产环境(是的,很多汗水),快速的马是创建一个单独的线程,使用池每隔... X/4 小时向数据库发送一个确定的查询。它使全新的防火墙/WAF 不会切断我的套接字连接!

解决方案 2?检查基础设施。检查连续性。检查网络接口速度和模式的一致性(例如全双工、100M)。检查数据库服务器设置(无网卡节能嘿嘿)。并且可能保持解决方案 1 中的探针正常工作。

编辑。 testOnBorrow 和 validationQuery 在正常情况下应该可以工作。使用逻辑通道和物理套接字 btw 客户端和服务器对池进行映像。 testOnBorrow 在发送给您的请求之前检查通道是否有效。它使用validationQuery 来完成。

【讨论】:

谢谢。由于直到 8 小时不活动后才会发生这种情况,因此我无法确定添加验证查询是否解决了我的问题,或者是否还有更多问题。不过我很感激这些信息。 也许我描述的图像不够清晰,不足以强调 socket-conn 的事情。 testOnBorrow 可以验证 conn 但它无法检查 SOCKET。因此,使用驱动程序创建的对象返回了一些未由池正确管理的奇怪异常,因此您的 validationQuery 变得无用......我知道它是怎么回事。我不得不去客户那里待一个星期,一大早检查问题是否消失了嘿嘿嘿。无论如何,让我们知道进展如何! 对不起,这些都不是我擅长的。所以你说我的错误不会通过使用 ValidationQuery 来解决,因为问题实际上是其他东西杀死了连接而不是让它重新建立?有没有办法让 DBCP 强制连接保持打开状态?也许使用 timeBetweenEvictionRunsMillis 我相信默认为关闭?我在我们的情况下看到的唯一区别是,一旦发生这种情况,我就不会进行任何查询。第一,第二,没有。 我说,如果错误与深层物理问题(不是会话级别、应用级别但网络、链接或传输级别)有关,DBCP 无法解决,因为驱动程序中的工具不'没有提供足够的信息(我什至反编译了一个驱动程序嘿嘿嘿)。如果 conn 错误是合乎逻辑的,那么设置额外的参数将会很有效。让我们等待 :) --- 编辑:timeBtwEviction 与检查池中哪个 conn 空闲的事件之间的间隔有关,如果它足够老,就杀死它。

以上是关于Spring 应用程序在 8 小时后失去与 MySql 的连接。如何正确配置?的主要内容,如果未能解决你的问题,请参考以下文章

Spring 正在失去与数据库的连接,并且无法恢复或重新连接

Spring数据,MySQL,连接在8小时不活动后死亡

MySQL 数据库在 8 小时后断开连接。如何预防?

连接池(理论上应该是任意连接池) spring方法切入 mybatis redis等待请求 用了mysql连接的方法阻塞超过8小时导致mysql关闭连接 应用复活后用了已关闭连接而异常

django时间与系统时间差8小时

WildFly 8.2.0 - 失去与服务器的连接