【问题标题】:Tomcat connection pool & idle connectionsTomcat 连接池和空闲连接
【发布时间】:2014-12-04 12:37:31
【问题描述】:

我们正在开发一个网站使用

  • 雄猫 7
  • JDBC
  • PostgreSQL 9.2

我们有一些连接泄漏,并认为我们已经纠正了它们(数据库不再停止响应),但连接池的行为似乎仍然存在泄漏,因为我们有许多空闲连接大于上下文中设置的 maxIdle .xml。我想确保问题已得到解决。

出于测试目的,我使用以下 context.xml :

 <Resource
    auth="Container"
    name="jdbc/postgres"
    factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
    type="javax.sql.DataSource"

    username="admin"
    password="..."

    driverClassName="org.postgresql.Driver"
    url="jdbc:postgresql://127.0.0.1:5432/..."
    initialSize="1"
    maxActive="50"
    minIdle="0"
    maxIdle="3"
    maxWait="-1"
    minEvictableIdleTimeMillis="1000"
    timeBetweenEvictionRunsMillis="1000"
    />

如果我理解正确,我们应该在启动时有 1 个空闲连接,并且根据负载从 0 到 3,对吧?

发生的情况是:启动时有 1 个连接,如果负载低,则最多 3 个空闲连接,高负载后空闲连接超过 3 个。然后这些连接不会立即关闭,我们不知道它们何时/是否会关闭(有时其中一些会关闭)。

所以问题是:这种行为正常吗?

感谢您的帮助

EDIT:添加了工厂属性,并没有改变问题

编辑 2 :使用 removeAbandoned 和 removeAbandonedTimeout 使空闲连接在每个 removeAbandonedTimeout 时关闭。所以我们可能仍然有一些连接泄漏。以下是我们用来连接数据库并执行请求的一些代码:

PostgreSQLConnectionProvider,只是一个提供连接的静态类:

public class PostgreSQLConnectionProvider {

    public static Connection getConnection() throws NamingException, SQLException {

        String dsString = "java:/comp/env/jdbc/postgres";
        Context context = new InitialContext();
        DataSource ds = (DataSource) context.lookup(dsString);
        Connection connection = ds.getConnection();

        return connection;
    }
}

DAO抽象类:

public abstract class DAO implements java.lang.AutoCloseable {

    // Private attributes :
    private Connection _connection;

    // Constructors :
    public DAO() {

        try { _connection = PostgreSQLConnectionProvider.getConnection(); }
        catch (NamingException | SQLException ex) { 
            Logger.getLogger(DAO.class.getName()).log(Level.SEVERE, null, ex); 
        }
    }

    // Getters :
    public Connection getConnection() { return _connection; }

    // Closeable :
    @Override
    public void close() throws SQLException {

        if(!_connection.getAutoCommit()) {

            _connection.rollback();
            _connection.setAutoCommit(true);
        }

        _connection.close();
    }
}

UserDAO,一个小的 DAO 子类(我们有几个 DAO 子类来请求数据库):

public class UserDAO extends DAO {

    public User getUserWithId(int id) throws SQLException {

        PreparedStatement ps = null;
        ResultSet rs = null;

        User user = null;

        try {

            String sql = "select * from \"USER\" where id_user = ?;";

            ps = getConnection().prepareStatement(sql);
            ps.setInt(1, id);

            rs = ps.executeQuery();
            rs.next();

            String login = rs.getString("login");
            String password = rs.getString("password");
            String firstName = rs.getString("first_name");
            String lastName = rs.getString("last_name");
            String email = rs.getString("email");

            user = new User(id, login, password, firstName, lastName, email);
        }
        finally {

            if(rs != null) rs.close();
            if(ps != null) ps.close();
        }

        return user;
    }
}

DAO 子类使用示例:

try(UserDAO dao = new UserDAO()) {

    try {

        User user = dao.getUserWithId(52);
    }
    catch (SQLException ex) {

        // Handle exeption during getUserWithId
    }
}
catch (SQLException ex) {

    // Handle exeption during dao.close()
}

【问题讨论】:

  • 尝试使用tomcat的连接池实现——功能更丰富,文档也不错people.apache.org/~fhanik/jdbc-pool/jdbc-pool.html
  • 谢谢,不过这已经是我在做的了(你可以在context.xml中看到我使用的参数)。
  • 我看不到主要部分 - factory 属性和适当的类名
  • 哎呀你是对的,这个属性是必需的,我没有看到它。但是添加它并没有改变任何东西(我的参数已经被考虑在内:始终尊重initialSize)。
  • 使用 removeAbandoned 和 removeAbandonedTimeout 会使空闲连接在每次 removeAbandonedTimeout 时消失,所以我想我们仍然有连接泄漏:/ .

标签: java postgresql tomcat connection-pooling


【解决方案1】:

查看代码,似乎连接在 DAO 的生命周期内被抓取,而不是语句的生命周期,这是通常的预期。通常,您会在即将执行语句时从池中获取一个连接,并在完成后对其调用 close() 以将其返回到池中。

此外,在 finally 子句中,rs.close()ps.close() 都可能引发异常,从而导致错过对准备好的语句的最后一次调用。

在 Java 7 中,您还可以使用 try with resources 语句来关闭准备好的语句和连接。根据规范,当语句关闭时,驱动程序应该为您关闭结果。

【讨论】:

  • 感谢您的回答。是的,连接在 DAO 的生命周期内被抓取,但 DAO 在几行后关闭,我不认为这是一个问题,不是吗?您对可以抛出异常的 close() 是正确的,我将它们包装在一个 try-catch 中(我更喜欢自己调用 rs.close() 而不是依赖驱动程序的实现)。我要到下周才能测试,如果 close() 包装改变了什么,我会告诉你的。
  • 如果您为每次调用 getUserWithId() 实例化 DAO,那么是的,应该没问题。
猜你喜欢
  • 2011-11-13
  • 1970-01-01
  • 2012-04-02
  • 2013-03-22
  • 1970-01-01
  • 2012-01-15
  • 2016-07-11
  • 2010-11-25
  • 1970-01-01
相关资源
最近更新 更多