【问题标题】:JDBC MySql connection pooling practices to avoid exhausted connection poolJDBC MySql 连接池实践避免连接池耗尽
【发布时间】:2011-01-19 18:28:33
【问题描述】:

我在 GlassFish 上有一个 Java-JSF Web 应用程序,我想在其中使用连接池。因此,我创建了一个 application 作用域 bean,它与 Connection 实例一起为其他 bean 服务:

public class DatabaseBean {

    private DataSource myDataSource;

    public DatabaseBean() {
        try {
            Context ctx = new InitialContext();
            ecwinsDataSource = (DataSource) ctx.lookup("jdbc/myDataSource");
        } catch (NamingException ex) {
            ex.printStackTrace();
        }
    }

    public Connection getConnection() throws ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException {
        Connection connection = myDataSource.getConnection();
        System.out.println("Succesfully connected: " + connection);
        //Sample: Succesfully connected: com.sun.gjc.spi.jdbc40.ConnectionHolder40@7fb213a5
        return connection;
    }
}

这样连接池很快就会被填满;在“db-related”视图中导航几次后,应用程序停止并显示以下内容:

RAR5117:无法从连接池 [mysql_testPool] 获取/创建连接。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。 RAR5114:分配连接时出错:[分配连接时出错。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。] java.sql.SQLException:分配连接时出错。原因:使用中的连接等于 max-pool-size 和过期的 max-wait-time。无法分配更多连接。

我正在关闭所有方法中的连接和其他资源。应用程序通过独立连接运行一切正常。

我做错了什么?任何提示或建议将不胜感激。

【问题讨论】:

    标签: java mysql jdbc glassfish connection-pooling


    【解决方案1】:

    该异常表示应用程序代码泄漏数据库连接的典型案例。您需要确保以完全相同的方法在try-with-resources 块中获取关闭所有它们(ConnectionStatement ResultSet)按照正常的 JDBC 习惯进行阻塞。

    public void create(Entity entity) throws SQLException {
        try (
            Connection connection = dataSource.getConnection();
            PreparedStatement statement = connection.prepareStatement(SQL_CREATE);
        ) { 
            statement.setSomeObject(1, entity.getSomeProperty());
            // ...
            statement.executeUpdate();
        }
    }
    

    或者当您不在 Java 7 上时,在 try-finally 块中。在finally 中关闭它们将保证它们在出现异常时也被关闭。

    public void create(Entity entity) throws SQLException {
        Connection connection = null;
        PreparedStatement statement = null;
    
        try { 
            connection = dataSource.getConnection();
            statement = connection.prepareStatement(SQL_CREATE);
            statement.setSomeObject(1, entity.getSomeProperty());
            // ...
            statement.executeUpdate();
        } finally {
            if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {}
            if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {}
        }
    }
    

    是的,即使使用连接池,您仍然需要自己关闭连接。初学者中的一个常见错误是他们认为它会自动处理关闭。这不正确。连接池即返回一个包装的连接,它在 close() 中执行以下操作:

    public void close() throws SQLException {
        if (this.connection is still eligible for reuse) {
            do not close this.connection, but just return it to pool for reuse;
        } else {
            actually invoke this.connection.close();
        }
    }
    

    不关闭它们会导致连接不会被释放回池以供重用,因此它将一次又一次地获取新的连接,直到数据库用完连接,这将导致您的应用程序崩溃。

    另见:

    【讨论】:

    • 感谢您的详尽回答,这真的很有帮助!我用 app-modifications 编辑了我的评论。
    • 再次感谢您的精彩信息!
    【解决方案2】:

    如果您需要 JDBC 连接池,为什么不依赖现有的呢? AFAIK,JDBC 连接池或多或少被认为是这些 Java 应用程序服务器中的标准功能,而 IMO,如果您只是对创建应用程序感兴趣,您不应该自己构建它。

    这是一个可以帮助您入门的链接: http://weblogs.java.net/blog/2007/09/12/totd-9-using-jdbc-connection-pooljndi-name-glassfish-rails-application

    您可能应该做的是找出如何让您的应用程序使用 jndi 从池中获取连接。

    【讨论】:

    • 他已经这样做了?他已经在 appserver 中配置了一个连接池数据源。他只是没有按照异常正确关闭资源。
    猜你喜欢
    • 2017-07-10
    • 1970-01-01
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2022-07-24
    • 2011-07-04
    相关资源
    最近更新 更多