我有一个问题,我在用户通过tomcat web应用程序请求重置我的MySQL数据库后得到了tomcat异常。到目前为止,我已尝试将其分解为设置,问题和分析,以帮助任何人尝试阅读此内容。
重置基本上包括从java代码调用bash脚本:
用户启动的过程通常会将数据库还原到以前的状态,但它也可用于从其他系统导入数据库。一切都完成后,用户然后尝试访问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脚本(例如使用某种类型的命令行实用程序)执行此操作。问题是,我不知道该怎么做,或者甚至可能。
我发现了一些关于拦截器的文档,但我不确定这是否适用于重置连接。我会继续调查。
感谢大家的时间和帮助!
答案 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