繁体   English   中英

在Tomcat Web应用程序中使用Java和MySQL进行连接池

[英]Connection pooling with Java and MySQL in Tomcat web application

我最近编写并部署了一个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是因为我使用了类似的名称。

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

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

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

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

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

为什么使用MySQL连接池? 当我进行JNDI查找和连接池时,我更喜欢Apache DBCP库。 我发现它运作良好。

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

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

更新:

Google提出了与您同名的Oracle类。 现在我真的不喜欢你的代码,因为当你可以轻松获得更好的替代品时,你自己写了一些东西。 我马上把你丢弃,用DBCP和JNDI重做。

此错误表示服务器意外关闭连接。 这可能发生在以下2个案例中,

  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()"

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

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

如果你没有找到答案,那么我最后一天一直在处理这个问题。 我基本上都在做同样的事情,除了我的基础是apache.commons.pool。 你看到EOF的确切错误。 检查最有可能在数据目录中的mysqld错误日志文件。 寻找mysqld崩溃。 mysqld_safe会在崩溃时快速重新启动你的mysqld,所以除非你查看它的日志文件,否则不会发生这种情况。 / var / log对此方案没有帮助。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM