繁体   English   中英

如何检查Java EE应用程序中的数据库连接泄漏?

[英]How to check the Database Connection leakage in Java EE application?

有没有办法检查Java EE应用程序中的连接泄漏?

应用程序在我的本地计算机上运行。 它使用MySQL数据库,用户将其详细信息输入此数据库。

在我看来,连接泄漏意味着没有正确关闭连接对象。 我在我的应用程序中创建了太多的数据库连接。 我想检查数据库连接中是否存在任何连接泄漏。

log4jdbc是一个可以记录其他JDBC驱动程序的SQL和/或JDBC调用的Java JDBC驱动程序,它有一个记录器,用于记录连接打开和关闭事件以及转储所有打开的连接号。 这对于追踪连接泄漏问题非常有用

您可能想要检查的另一个工具是ConnLeakFinder一个在java代码中查明jdbc连接泄漏的简单工具 虽然我没有任何经验。

如果您正在使用Java EE应用服务器,则应该能够将其配置为在外出时检查连接,并在它们不返回时回收过时的连接。

连接泄漏确实是一个问题。 如果您在代码中的许多地方分散了连接管理,我会担心找到它们是一个很大的问题。 我希望看到一个仅在定义良好的持久层中使用的Java EE连接池。 连接应该由管理该工作单元的事务的服务层打开,并在用例结束后立即关闭,在finally块的方法范围内。

如果那不是真的,我认为现在是重构的时候了。

尝试使用FindBug 它是一个静态代码分析工具,可用作eclipse插件以及独立应用程序。 除了连接泄漏之外,它还会在您的应用程序中发现其他问题

使用连接工厂,例如:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class ConnectionFactory {
    private static Connection connection;

    public static synchronized Connection getConnection() throws SQLException {
        if (connection == null || connection.isClosed()) {
            connection = DriverManager.getConnection("url");
        }
        return connection;
    }
}

这样你就永远不会留下无人看管的联系。 如果需要多个连接,请使用连接池(性能)。 大多数应用程序服务器都具有JDBC连接池功能。

解决连接泄漏的最佳方法是在测试期间执行此操作

您可以使用自动化实用程序,以便每个测试验证是否存在连接泄漏。

@BeforeClass
public static void initConnectionLeakUtility() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil = new ConnectionLeakUtil();
    }
}

@AfterClass
public static void assertNoLeaks() {
    if ( enableConnectionLeakDetection ) {
        connectionLeakUtil.assertNoLeaks();
    }
}

ConnectionLeakUtil看起来像这样:

public class ConnectionLeakUtil {

    private JdbcProperties jdbcProperties = JdbcProperties.INSTANCE;

    private List idleConnectionCounters = 
        Arrays.asList(
            H2IdleConnectionCounter.INSTANCE,
            OracleIdleConnectionCounter.INSTANCE,
            PostgreSQLIdleConnectionCounter.INSTANCE,
            MySQLIdleConnectionCounter.INSTANCE
    );

    private IdleConnectionCounter connectionCounter;

    private int connectionLeakCount;

    public ConnectionLeakUtil() {
        for ( IdleConnectionCounter connectionCounter : 
            idleConnectionCounters ) {
            if ( connectionCounter.appliesTo( 
                Dialect.getDialect().getClass() ) ) {
                this.connectionCounter = connectionCounter;
                break;
            }
        }
        if ( connectionCounter != null ) {
            connectionLeakCount = countConnectionLeaks();
        }
    }

    public void assertNoLeaks() {
        if ( connectionCounter != null ) {
            int currentConnectionLeakCount = countConnectionLeaks();
            int diff = currentConnectionLeakCount - connectionLeakCount;
            if ( diff > 0 ) {
                throw new ConnectionLeakException( 
                    String.format(
                        "%d connection(s) have been leaked! Previous leak count: %d, Current leak count: %d",
                        diff,
                        connectionLeakCount,
                        currentConnectionLeakCount
                    ) 
                );
            }
        }
    }

    private int countConnectionLeaks() {
        try ( Connection connection = newConnection() ) {
            return connectionCounter.count( connection );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }

    private Connection newConnection() {
        try {
            return DriverManager.getConnection(
                jdbcProperties.getUrl(),
                jdbcProperties.getUser(),
                jdbcProperties.getPassword()
            );
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

IdleConnectionCounter实现可以在这篇博文中找到,MySQL版本如下:

public class MySQLIdleConnectionCounter implements IdleConnectionCounter {

    public static final IdleConnectionCounter INSTANCE = 
        new MySQLIdleConnectionCounter();

    @Override
    public boolean appliesTo(Class<? extends Dialect> dialect) {
        return MySQL5Dialect.class.isAssignableFrom( dialect );
    }

    @Override
    public int count(Connection connection) {
        try ( Statement statement = connection.createStatement() ) {
            try ( ResultSet resultSet = statement.executeQuery(
                    "SHOW PROCESSLIST" ) ) {
                int count = 0;
                while ( resultSet.next() ) {
                    String state = resultSet.getString( "command" );
                    if ( "sleep".equalsIgnoreCase( state ) ) {
                        count++;
                    }
                }
                return count;
            }
        }
        catch ( SQLException e ) {
            throw new IllegalStateException( e );
        }
    }
}

现在,当您运行测试时,在连接泄露时会出现故障。

暂无
暂无

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

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