簡體   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