我最近编写并将Java Web应用程序部署到服务器,我发现了一个在开发或测试期间没有出现的异常问题。
当用户长时间登录并从数据库显示数据后,该页面表明没有要查看的记录。但是在页面刷新时,第一个 x 记录将根据分页规则显示。
检查日志,我发现:
ERROR|19 09 2009|09 28 54|http-8080-4|myDataSharer.database_access.Database_Metadata_DBA| - Error getting types of columns of tabular Dataset 12
com.mysql.jdbc.CommunicationsException: Communications link failure due to underlying exception:
** BEGIN NESTED EXCEPTION **
java.io.EOFException
STACKTRACE:
java.io.EOFException
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1956)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2368)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:2867)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1616)
等几百行。
该应用程序目前设置为约100个用户,但尚未完全使用。它使用Apache Tomcat servlets / jsps和MySQL数据库之间的连接池,使用以下代码示例构成数据库操作的一般安排,其中每页通常有几个:
// Gets a Dataset.
public static Dataset getDataset(int DatasetNo) {
ConnectionPool_DBA pool = ConnectionPool_DBA.getInstance();
Connection connection = pool.getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
String query = ("SELECT * " +
"FROM Dataset " +
"WHERE DatasetNo = ?;");
try {
ps = connection.prepareStatement(query);
ps.setInt(1, DatasetNo);
rs = ps.executeQuery();
if (rs.next()) {
Dataset d = new Dataset();
d.setDatasetNo(rs.getInt("DatasetNo"));
d.setDatasetName(rs.getString("DatasetName"));
...
}
return d;
}
else {
return null;
}
}
catch(Exception ex) {
logger.error("Error getting Dataset " + DatasetNo + "\n", ex);
return null;
}
finally {
DatabaseUtils.closeResultSet(rs);
DatabaseUtils.closePreparedStatement(ps);
pool.freeConnection(connection);
}
}
是否有人能够提出纠正此问题的方法?
我认为这是由于MySQL将连接轮询连接打开长达八小时,但我不确定。
由于
Martin O'Shea。
为了澄清关于我的连接池方法的一点,我在我的应用程序中使用的不是Oracle,而是我自己的类如下:
package myDataSharer.database_access;
import java.sql.*;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import org.apache.log4j.Logger;
public class ConnectionPool_DBA {
static Logger logger = Logger.getLogger(ConnectionPool_DBA.class.getName());
private static ConnectionPool_DBA pool = null;
private static DataSource dataSource = null;
public synchronized static ConnectionPool_DBA getInstance() {
if (pool == null) {
pool = new ConnectionPool_DBA();
}
return pool;
}
private ConnectionPool_DBA() {
try {
InitialContext ic = new InitialContext();
dataSource = (DataSource) ic.lookup("java:/comp/env/jdbc/myDataSharer");
}
catch(Exception ex) {
logger.error("Error getting a connection pool's datasource\n", ex);
}
}
public void freeConnection(Connection c) {
try {
c.close();
}
catch (Exception ex) {
logger.error("Error terminating a connection pool connection\n", ex);
}
}
public Connection getConnection() {
try {
return dataSource.getConnection();
}
catch (Exception ex) {
logger.error("Error getting a connection pool connection\n", ex);
return null;
}
}
}
我认为提到Oracle是因为我使用了类似名称。
答案 0 :(得分:4)
有一些关于避免这种情况的指针,从其他来源获得,特别是来自其他驱动程序和其他应用程序服务器的连接池实现。有些信息已在JNDI数据源的Tomcat文档中提供。
就应用程序的行为而言,用户可能会看到池的结果第一次返回到应用程序的陈旧连接。第二次,池可能返回一个可以为应用程序的查询提供服务的不同连接。
Tomcat JNDI数据源基于Commons DBCP,因此适用于DBCP的configuration properties也适用于Tomcat。
答案 1 :(得分:3)
我想知道为什么你在代码中使用ConnectionPool_DBA而不是让Tomcat处理池并简单地使用JNDI查找连接。
为什么使用MySQL连接池?当我进行JNDI查找和连接池时,我更喜欢Apache DBCP库。我发现它运作良好。
我还会问你的DatabaseUtils方法是否抛出任何异常,因为如果你调用pool.freeConnection()之前的任何一个调用抛出一个,你将永远不会释放该连接。
我不太喜欢你的代码,因为执行SQL操作的类应该将Connection实例传递给它,并且不应该具有获取和使用Connection的双重责任。持久化类无法知道它是否在更大的事务中使用。最好有一个单独的服务层来获取Connection,管理事务,编组持久化类,并在它完成时进行清理。
更新:
谷歌打开了与你同名的Oracle类。现在我真的不喜欢你的代码,因为当你可以轻松获得更好的替代品时,你自己写了一些东西。我马上把你丢弃,用DBCP和JNDI重做。答案 2 :(得分:2)
此错误表示服务器意外关闭了连接。这可能发生在以下2个案例中,
MySQL会在一段时间后关闭空闲连接(默认为8小时)。发生这种情况时,没有线程负责关闭连接,因此它变得陈旧。如果此错误仅在长时间闲置后发生,则很可能是这种情况。
如果您没有完全读取所有响应,则连接可能会在忙碌状态下返回到池中。下一次,命令被发送到MySQL并关闭错误状态的连接。如果错误发生频繁,这可能是原因。
同时,设置逐出线程将有助于缓解问题。将这样的内容添加到数据源
...
removeAbandoned="true"
removeAbandonedTimeout="120"
logAbandoned="true"
testOnBorrow="false"
testOnReturn="false"
timeBetweenEvictionRunsMillis="60000"
numTestsPerEvictionRun="5"
minEvictableIdleTimeMillis="30000"
testWhileIdle="true"
validationQuery="select now()"
答案 3 :(得分:0)
Web服务器和数据库之间是否存在透明地关闭空闲TCP / IP连接的路由器?
如果是这样,您必须让连接池从池中丢弃未使用的超过XX分钟的连接,或者在连接上每隔YY分钟执行某种ping操作以使其保持活动状态。
答案 4 :(得分:0)
关于你没有找到答案的关闭机会我在最后一天一直在处理这个问题。我基本上都在做同样的事情,除了我的基础是apache.commons.pool。你看到EOF的确切错误。检查最有可能在数据目录中的mysqld错误日志文件。寻找mysqld崩溃。 mysqld_safe会在崩溃时快速重新启动你的mysqld,所以除非你查看它的日志文件,否则不会发生这种情况。 / var / log对这种情况没有帮助。
崩溃前创建的连接将在崩溃后进行EOF。