最近工作发现,有个项目偶尔会报Couldn’t rollback jdbc connection. No operations allowed after connection closed,项目技术选型如下:
数据库mysql
连接池druid
quartz
从异常来看,是jdbc连接关闭后,代码中依然拿出来使用了,从而报错。进一步了解到,mysql连接默认的最长维持时间是8小时,即建立连接后,如果8小时内客户端没关闭连接,mysql就会把连接关闭,如果客户端拿到了这个连接,就会报错了。这种情况一般会发生在连接池的情况下,因为在连接池中,为了提高资源利用率,连接使用完之后一般不会关闭,而是返回连接池中,等待下次使用。
网上提供的解决方法如下:
延长mysql的连接维持时间,需要修改mysql配置;
从连接池中获取连接时,校验连接的有效性。
以上两种方法一个是从服务端出发,一个是从客户端出来。由于mysql权限问题,无法直接修改mysql的配置,因此只能从客户端入手了。
项目中使用的连接池是druid,理所当然是druid的问题,baidu了下一圈下来,druid相关的设置如下:
<!-- 获取连接时检查连接是否可用,官方说会影响性能,不建议开启-->
<property name="testOnBorrow" value="false" />
<!-- 归还连接时检查连接是否可用,官方说会影响性能,不建议开启-->
<property name="testOnReturn" value="false" />
<!-- 空闲时检查,再配合多久检查一次,官方建议使用的方式 -->
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 检查语句 -->
<property name="validationQuery" value="SELECT 'x'" />
对照项目中的配置发现,两者基本一致。看来问题并不在druid。
再分析异常信息,发现报错都是在quartz相关的类中,因此将焦点转向了quartz。
在quartz中,发现有一段配置是这样的:
<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">50</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<prop key="org.quartz.jobStore.dataSource">member</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<!-- quartz数据库配置 -->
<prop key="org.quartz.dataSource.member.driver">com.mysql.jdbc.Driver</prop>
<prop key="org.quartz.dataSource.member.URL">${member.jdbc.url}</prop>
<prop key="org.quartz.dataSource.member.user">${member.jdbc.username}</prop>
<prop key="org.quartz.dataSource.member.password">${member.jdbc.password}</prop>
<prop key="org.quartz.dataSource.member.maxConnections">10</prop>
</props>
</property>
</bean>
这里可以发现,quartz并没有使用druid连接池,而是重新开启数据库连接。在quartz 2.2.1 jdbc 连接池参数配置中,知道了quartz默认的连接是c3p0,并且了解了相关配置项。继续搜索,发现有人也踩过这个坑:quartz和数据库断连接的解决办法,结合这两篇文章,我给出的解决方案如下:
<bean id="schedulerFactoryBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
<property name="quartzProperties">
<props>
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">50</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
<prop key="org.quartz.jobStore.dataSource">member</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.dataSource.member.driver">com.mysql.jdbc.Driver</prop>
<prop key="org.quartz.dataSource.member.URL">${member.jdbc.url}</prop>
<prop key="org.quartz.dataSource.member.user">${member.jdbc.username}</prop>
<prop key="org.quartz.dataSource.member.password">${member.jdbc.password}</prop>
<prop key="org.quartz.dataSource.member.maxConnections">10</prop>
<!-- 添加的配置 -->
<prop key="org.quartz.dataSource.member.validateOnCheckout">true</prop>
<prop key="org.quartz.dataSource.member.validationQuery">SELECT 'x'</prop>
</props>
</property>
</bean>
只是在quartz配置文件中添加了两行:
<!-- 从连接池中获取连接时,验证连接是否有效 -->
<prop key="org.quartz.dataSource.member.validateOnCheckout">true</prop>
<!-- 验证语句 -->
<prop key="org.quartz.dataSource.member.validationQuery">SELECT 'x'</prop>
源著:https://my.oschina.net/funcy/blog/2045908