Как проверить утечку соединения с базой данных в приложении Java EE?

Есть ли способ проверить утечку соединения в приложении Java EE?

Приложение работает на моей локальной машине. Он использует базу данных MySQL, и пользователь вводит свои данные в эту базу данных.

На мой взгляд, утечка соединения означает неправильное закрытие объекта соединения. Я создаю слишком много подключений к базе данных в своем приложении. Я хочу проверить, есть ли утечка соединения в соединениях с базой данных.


person User 1034    schedule 19.06.2010    source источник


Ответы (5)


log4jdbc, драйвер Java JDBC, который может регистрировать вызовы SQL и/или JDBC для других драйверов JDBC, имеет регистратор, который записывает события открытия и закрытия соединения, а также выводит все номера открытых соединений. Это очень полезно для поиска проблем с утечкой соединения.

Другой инструмент, который вы, возможно, захотите проверить, — это ConnLeakFinder, простой инструмент для точного определения утечек соединения jdbc в java. код. Хотя у меня нет никакого опыта в этом.

person Pascal Thivent    schedule 19.06.2010
comment
Как это соотносится с p6spy? - person Thorbjørn Ravn Andersen; 19.06.2010
comment
@ Thorbjørn Я считаю его более современной альтернативой (последний выпуск P6Spy был 7+ лет назад): он использует SLF4J, предлагает некоторые функции и параметры конфигурации, которые мне нравятся, он поддерживает JDBC 4... Я использую его как замена сейчас. - person Pascal Thivent; 19.06.2010
comment
... и ConnLeakFinder не обновлялся с 2009 года :) Интересно, почему это происходит с этими инструментами. - person Thorbjørn Ravn Andersen; 07.01.2014

Если вы используете сервер приложений Java EE, вы должны иметь возможность настроить его так, чтобы он проверял соединения, когда они уходят, и восстанавливал устаревшие соединения, когда они не возвращаются.

Утечка соединения действительно является проблемой. Я был бы обеспокоен, если бы управление соединениями было разбросано по стольким местам в коде, что было бы большой проблемой найти их все. Я ожидаю увидеть пул соединений Java EE, который использовался только в пределах четко определенного уровня сохраняемости. Соединения должны открываться сервисным уровнем, который управляет транзакцией для этой единицы работы и закрывает ее, как только вариант использования завершается, в пределах области действия метода в блоке finally.

Если это не так, я думаю, пришло время провести рефакторинг.

person duffymo    schedule 19.06.2010

попробуйте использовать FindBug. Это инструмент статического анализа кода, доступный как в виде плагина для eclipse, так и в виде отдельного приложения. Помимо утечки соединения, он также найдет другие проблемы в вашем приложении.

person Vinay Lodha    schedule 19.06.2010

Используйте фабрику соединений, например:

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.

person Patrick Holthuizen    schedule 19.06.2010

Лучший способ обнаружить утечку соединения — сделать это во время тестирования. .

Вы можете использовать автоматизированную утилиту, чтобы каждый тест проверял наличие утечки соединения.

@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 );
        }
    }
}

Теперь, когда вы запускаете свои тесты, вы получите ошибку при утечке соединения.

person Vlad Mihalcea    schedule 12.07.2016