3

我最近编写了一个 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 将连接轮询连接打开长达八小时,但不确定。

谢谢

马丁奥谢。


只是为了澄清关于我的连接池方法的一点,我在我的应用程序中使用的不是 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 是因为我使用了类似的名称。

4

5 回答 5

4

从其他来源,特别是从其他驱动程序的连接池实现和其他应用程序服务器中获得了一些关于避免这种情况的建议。JNDI 数据源的 Tomcat 文档中已经提供了一些信息。

  1. 建立一个清理/收割计划,如果池中的连接超过一定时间不活动,则将关闭它们。将与数据库的连接保持打开 8 小时(MySQL 默认值)不是一个好习惯。在大多数应用程序服务器上,非活动连接超时值是可配置的,通常小于 15 分钟(即连接不能在池中停留超过 15 分钟,除非它们被一次又一次地重用)。在 Tomcat 中,当使用 JNDI 数据源时,使用 removeAbandoned 和 removeAbandonedTimeout 设置来做同样的事情。
  2. 当新连接从池返回到应用程序时,请确保首先对其进行测试。例如,我知道的大多数应用程序服务器都可以配置为通过执行“SELECT 1 FROM dual”来测试与 Oracle 数据库的连接。在 Tomcat 中,使用 validationQuery 属性为 MySQL 设置适当的查询 - 我相信这是“SELECT 1”(不带引号)。设置 validationQuery 属性的值有帮助的原因是,如果查询执行失败,连接将从池中删除,并在其位置创建新连接。

就您的应用程序的行为而言,用户可能第一次看到池返回到应用程序的陈旧连接的结果。第二次,池可能会返回一个不同的连接,可以为应用程序的查询提供服务。

Tomcat JNDI 数据源基于 Commons DBCP,因此适用于 DBCP 的配置属性也将适用于 Tomcat。

于 2009-09-19T17:27:34.013 回答
3

我想知道为什么您在代码中使用 ConnectionPool_DBA 而不是让 Tomcat 处理池并简单地使用 JNDI 查找连接。

为什么要在 MySQL 中使用 Oracle 连接池?当我进行 JNDI 查找和连接池时,我更喜欢 Apache DBCP 库。我发现它工作得很好。

我还会询问您的 DatabaseUtils 方法是否抛出任何异常,因为如果在调用 pool.freeConnection() 之前的任何一个调用都抛出一个异常,那么您将永远不会释放该连接。

我不太喜欢您的代码,因为执行 SQL 操作的类应该将其 Connection 实例传递给它,并且不应该承担获取和使用 Connection 的双重责任。持久性类无法知道它是否正在更大的事务中使用。最好有一个单独的服务层来获取连接、管理事务、编组持久性类并在完成时进行清理。

更新:

谷歌找到了与你同名的 Oracle 类。现在我真的不喜欢你的代码,因为当一个更好的替代方案很容易获得时,你自己写了一些东西。我会立即放弃你的,并使用 DBCP 和 JNDI 重做。

于 2009-09-19T17:24:28.653 回答
2

This error indicates server closes connection unexpectedly. This can occur in following 2 cases,

  1. MySQL closes idle connection after certain time (default is 8 hours). When this occurs, no thread is responsible for closing the connection so it gets stale. This is most likely the cause if this error only happens after long idle.

  2. If you don't completely read all the responses, the connection may get returned to the pool in busy state. Next time, a command is sent to MySQL and it closes connection for wrong state. If the error occurs quite frequent, this is probably the cause.

Meanwhile, setting up an eviction thread will help to alleviate the problem. Add something like this to the Data Source,

          ...
          removeAbandoned="true"
          removeAbandonedTimeout="120"
          logAbandoned="true"
          testOnBorrow="false"
          testOnReturn="false"
          timeBetweenEvictionRunsMillis="60000"
          numTestsPerEvictionRun="5"
          minEvictableIdleTimeMillis="30000"
          testWhileIdle="true"
          validationQuery="select now()"
于 2009-09-19T20:10:59.190 回答
0

如果你还没有找到答案,我在最后一天一直在处理这个问题。我基本上在做和你一样的事情,只是我的池基于 apache.commons.pool。您看到 EOF 的完全相同的错误。检查最有可能在数据目录中的 mysqld 错误日志文件。寻找 mysqld 崩溃。如果 mysqld_safe 崩溃,它将快速重新启动您的 mysqld,因此除非您查看其日志文件,否则这种情况不会很明显。/var/log 对这种情况没有帮助。

崩溃前创建的连接将在崩溃后 EOF。

于 2010-03-26T19:33:07.320 回答
0

Web 服务器和数据库之间是否有透明地关闭空闲 TCP/IP 连接的路由器?

如果是这样,您必须让您的连接池从池中丢弃超过 XX 分钟未使用的连接,或者每隔 YY 分钟对连接执行某种 ping 操作以使其保持活动状态。

于 2009-09-19T18:22:23.567 回答