【问题标题】:Connection pooling with Java and MySQL in Tomcat web application在 Tomcat Web 应用程序中使用 Java 和 MySQL 进行连接池
【发布时间】:2023-03-27 01:38:01
【问题描述】:

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

谢谢

马丁·奥谢。


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

【问题讨论】:

  • 您能否解释一下您是如何得出这是由于连接池中的错误导致的结论?我在任何地方都没有看到任何迹象......

标签: java mysql tomcat connection pooling


【解决方案1】:

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

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

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

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

更新:

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

【讨论】:

  • 由于时间和其他限制,这可能不太可能。不过还是谢谢。
  • 如果你不能解决这个问题并且它会再次发生,它会变得更有吸引力。
【解决方案2】:

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

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

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

Tomcat JNDI 数据源基于 Commons DBCP,因此适用于 DBCP 的configuration properties 也适用于 Tomcat。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 据我所知没有。我认为这是 Vineet 建议的设置。
    【解决方案4】:

    此错误表示服务器意外关闭连接。这可能发生在以下两种情况,

    1. MySQL 在一定时间后关闭空闲连接(默认为 8 小时)。发生这种情况时,没有线程负责关闭连接,因此它变得陈旧。如果此错误仅在长时间空闲后发生,这很可能是原因。

    2. 如果您没有完全阅读所有响应,连接可能会以忙碌状态返回到池中。下一次,一个命令被发送到 MySQL 并且它关闭错误状态的连接。如果错误经常发生,这可能是原因。

    同时,设置驱逐线程将有助于缓解问题。在数据源中添加类似这样的内容,

              ...
              removeAbandoned="true"
              removeAbandonedTimeout="120"
              logAbandoned="true"
              testOnBorrow="false"
              testOnReturn="false"
              timeBetweenEvictionRunsMillis="60000"
              numTestsPerEvictionRun="5"
              minEvictableIdleTimeMillis="30000"
              testWhileIdle="true"
              validationQuery="select now()"
    

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-23
        • 1970-01-01
        • 2013-04-25
        • 2018-04-07
        • 2015-12-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多