如何重置JDBC连接池

Tre*_*nin 5 java tomcat connection-pooling jdbc

我有一个问题,我在用户通过tomcat web应用程序请求重置我的MySQL数据库后得到了tomcat异常.到目前为止,我已尝试将其分解为设置,问题和分析,以帮助任何人尝试阅读此内容.

建立

重置基本上包括从java代码调用bash脚本到:

  • 删除root mysql用户密码
  • 加载旧版本的数据库
  • 在其上运行一些脚本
  • 恢复所有密码

它是用户启动的过程,通常将数据库还原到以前的状态,但它也用于从另一个系统导入数据库.一切都完成后,用户就会尝试访问Web应用程序的不同部分(即使用相同的会话而无需注销/重新登录),执行数据库查询以获取某些数据.

问题

一旦tomcat应用程序查询了DB,就会出现异常:

Dec 29, 2014 3:49:50 PM ERROR BasicSecurityRealm:216 - 
ERROR: ----- SQLException -----

Dec 29, 2014 3:49:50 PM  INFO BasicSecurityRealm:218 - Exceptioncom.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 234,810 milliseconds ago.  The last packet sent successfully to the server was 12 milliseconds ago.
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
...
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2540)
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2990)
Run Code Online (Sandbox Code Playgroud)

即使用户注销并重新登录,我也会看到此异常.如果我刷新页面四次​​,页面每次会加载一些不同的异常(上面的所有变体 - 由"EOFException:无法从服务器读取响应"引起的CommunicationsException).最后一次,一切似乎正常运行.

我唯一可以避免这些异常的事情是重启tomcat.我想避免这种情况,因为这意味着当前登录的用户将丢失其会话并且必须等待tomcat重新启动才能重新登录.强制他们注销/退回可能是一个可接受的妥协,但这无论如何都无法解决问题.

分析

据我所知,我认为问题与JDBC连接池有关.我正在使用JNDI数据源访问我的数据库,如下所示:

server.xml中:

  <GlobalNamingResources>
    <Resource name="jdbc/mydb"
              auth="Container"
              type="javax.sql.DataSource"
              maxActive="30" maxIdle="30" maxWait="2147483647"
              username="x" password="x"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/mydb?autoReconnect=true"/>
Run Code Online (Sandbox Code Playgroud)

web.xml中:

<!-- Data source definitions -->
<resource-ref>
    <res-ref-name>jdbc/mydb</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
    <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
Run Code Online (Sandbox Code Playgroud)

Java的:

    // Get connection to specified database
    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    DataSource ds = (DataSource) envCtx.lookup("jdbc/mydb");
    con = ds.getConnection();
    stmt = con.createStatement();
    rs = stmt.executeQuery("...");
Run Code Online (Sandbox Code Playgroud)

我相信连接池包含陈旧/死亡的连接.每当我与之建立连接时ds.getConnection,它就会获得这些旧连接之一.尝试使用它将在第一次失败并且连接被重置(注意我正在使用autoReconnect=true,所以第二次应该(并且确实)工作).但是,池中包含许多(在我的情况下,根据经验4或5)陈旧连接,因此在它们全部正确重置之前需要一段时间.重置连接后,一切都正常.

解决方案?

因为我使用autoReconnect=true我可以重新构造我的代码,这样如果我在尝试查询时遇到异常,我可以重试一次查询.如果它再次失败,那么我会知道确实存在问题.如果通过,则成功重新建立连接.

这个问题是代码中的任何地方都有查询.对它们进行重新分解会花费大量时间和测试,如果有必要我会做,但我们想避免.此外,如果查询由于其他原因而失败,则在报告之前将尝试两次.对于长查询,这可能会导致严重的用户体验延迟,但仅限于错误情况.

另一种解决方案是强制重置/重新连接连接池中的所有连接.我可以通过编程方式(即从调用bash脚本完成时的java代码)或bash脚本(例如使用某种类型的命令行实用程序)执行此操作.问题是,我不知道该怎么做,或者甚至可能.

我在Interceptor上找到了一些文档,但我不确定这是否适用于重置连接.我会继续调查.

感谢大家的时间和帮助!

小智 5

您可以在从池中获取之前测试连接

默认情况下,Tomcat <7 对Tomcat> = 7 使用commond-dbcp,它是jdbc-pool

在这两种情况下,都将下一个属性添加到连接池配置中:

validationQuery=<TEST SQL>
testOnBorrow=true
Run Code Online (Sandbox Code Playgroud)