如何重置JDBC连接池

时间:2014-12-29 17:15:15

标签: java tomcat jdbc connection-pooling

我有一个问题,我在用户通过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)

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

我唯一可以避免这些异常的事情就是重启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"/>

的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>

爪哇:

    // 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("...");

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

解决方案?

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

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

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

我发现了一些关于拦截器的文档,但我不确定这是否适用于重置连接。我会继续调查。

感谢大家的时间和帮助!

3 个答案:

答案 0 :(得分:5)

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

默认情况下,Tomcat&lt; 7使用commond-dbcp表示Tomcat&gt; = 7它jdbc-pool

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

validationQuery=<TEST SQL>
testOnBorrow=true

答案 1 :(得分:0)

由于您的项目中包含多个ojdbc之类的ojdbc6 or ojdbc14罐,因此将引发异常,这时您在尝试连接oracle 12c时删除所有{{ 1}} jars,然后添加任何一个jar,仅推荐一个为ojdbc jar。

答案 2 :(得分:0)

您可以先测试连接,然后再从池中获取。

添加连接池配置:

validationQuery=TEST SQL
testOnBorrow=true