Соединение Java ConnectionPool не закрывается, застряло в «спящем режиме» - PullRequest
4 голосов
/ 10 сентября 2008

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

Соединение работает нормально и возвращает запрос без проблем. Проблема в том, что соединение не закрывается должным образом и застревает в «спящем» режиме (по словам администратора MySQL). Это означает, что они становятся непригодными для использования, а затем у меня заканчиваются соединения.

Может кто-нибудь дать мне несколько советов относительно того, что я могу сделать, чтобы соединение вернулось к пулу успешно.

public class DatabaseBean {

private static final Logger logger = Logger.getLogger(DatabaseBean.class);

private Connection conn;
private PreparedStatement prepStmt;

/**
 * Zero argument constructor
 * Setup generic databse connection in here to avoid redundancy
 * The connection details are in /META-INF/context.xml
 */
public DatabaseBean() {
    try {
        InitialContext initContext = new InitialContext();
        DataSource ds = (DataSource) initContext.lookup("java:/comp/env/jdbc/mysite");
        conn = ds.getConnection();
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was a problem with the database connection.");
        logger.fatal(SQLEx);
        logger.fatal(SQLEx.getCause());
    }
    catch (NamingException nameEx) {
        logger.fatal("There was a naming exception");
        logger.fatal(nameEx);
        logger.fatal(nameEx.getCause());
    }
}

/**
 * Execute a query. Do not use for statements (update delete insert etc).
 *
 * @return A ResultSet of the execute query. A set of size zero if no results were returned. It is never null.
 * @see #executeUpdate() for running update, insert delete etc.
 */

public ResultSet executeQuery() {
    ResultSet result = null;
    try {
        result = prepStmt.executeQuery();
        logger.debug(prepStmt.toString());
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was an error running a query");
        logger.fatal(SQLEx);
    }
    return result;
}

СНИП

public void close() {
    try {
        prepStmt.close();
        prepStmt = null;

        conn.close();
        conn = null;
    } catch (SQLException SQLEx) {
        logger.warn("There was an error closing the database connection.");
    }
}
}

Это внутри javabean, который использует соединение с базой данных.

public LinkedList<ImportantNoticeBean> getImportantNotices() {

    DatabaseBean noticesDBBean = new DatabaseBean();
    LinkedList<ImportantNoticeBean> listOfNotices = new LinkedList<ImportantNoticeBean>();

    try {
        PreparedStatement preStmt = noticesDBBean.getConn().prepareStatement("SELECT pseudonym, message, date_to, date_from " +
                "FROM importantnotices, users " +
                "WHERE importantnotices.username = users.username " +
                "AND NOW() >= date_from AND NOW() <= date_to;");

        noticesDBBean.setPrepStmt(preStmt);
        ResultSet result = noticesDBBean.executeQuery();

        while (result.next()) {
            ImportantNoticeBean noticeBean = new ImportantNoticeBean();

            noticeBean.setAuthor(result.getString("pseudonym"));
            noticeBean.setMessage(result.getString("message"));
            noticeBean.setDateTo(result.getDate("date_to"));
            noticeBean.setDateFrom(result.getDate("date_from"));

            listOfNotices.add(noticeBean);
        }

        result.close();

    } catch (SQLException SQLEx) {
        logger.error("There was an error in ImportantNoticesBean.getImportantNotices()");
        logger.error(SQLEx);
    } finally {
        noticesDBBean.close();
    }
    return listOfNotices;
}

<Context reloadable="true">

    <Resource name="jdbc/mysite"
              auth="Container"
              type="javax.sql.DataSource"
              username="user"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/mysite"
              maxActive="10"
              maxIdle="5"
              maxWait="6000"
              removeAbandoned="true"
              logAbandoned="false"
              removeAbandonedTimeout="20"
            />
</Context>

Ответы [ 6 ]

2 голосов
/ 12 сентября 2008

Хорошо, я мог бы это отсортировать. Я изменил ресурс конфигурации базы данных следующим образом:

*SNIP*
maxActive="10"
maxIdle="5"
maxWait="7000"
removeAbandoned="true"
logAbandoned="false"
removeAbandonedTimeout="3"
*SNIP*

Пока это работает достаточно хорошо. Что происходит, афаик, так это то, что, как только я достигаю десяти соединений, Tomcat проверяет наличие оставленных соединений (время простоя> 3). Это делается в пакетном задании каждый раз, когда достигается максимальное количество соединений. Потенциальная проблема заключается в том, что мне нужно выполнить более 10 запросов одновременно (не уникально для меня). Важно то, что removeAbandonedTimeout меньше maxWait.

Это то, что должно происходить? т.е. это способ, которым пул должен работать? Если это кажется, по крайней мере мне, что вы должны подождать, пока что-то (соединение) прервется, прежде чем исправить, вместо того, чтобы не дать ему «сломаться» в первую очередь. Может быть, я все еще не понимаю.

2 голосов
/ 10 сентября 2008

Это похожий вопрос - Настройки пула подключений для Tomcat

Это мой ответ на этот вопрос, и он решил проблему для другого парня. Это тоже может вам помочь.

Документация Tomcat

DBCP использует пул соединений с базой данных Jakarta-Commons. Он зависит от количества компонентов Jakarta-Commons:

* Jakarta-Commons DBCP
* Jakarta-Commons Collections
* Jakarta-Commons Pool

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

testWhileIdle=true
timeBetweenEvictionRunsMillis=300000
2 голосов
/ 10 сентября 2008

Вы, похоже, правильно закрываете соединение - за исключением случая, когда prepStmt.close () выдает исключение SQLException, я не могу найти утечку соединения.

Какую реализацию пула вы используете? Когда вы закрываете соединение, пулу не нужно немедленно закрывать базовое соединение MySQL - ведь это точка пула соединений! Таким образом, со стороны MySQL, соединения будут выглядеть живыми, хотя ваше приложение не использует их; они могут просто храниться в пуле соединений TC.

Возможно, вы захотите поэкспериментировать с настройками пула соединений. Попросите его уменьшить пул во время простоя системы. Или попросите его периодически обновлять все соединения. Или установите строгую верхнюю границу для числа одновременных соединений, которые он когда-либо получает от MySQL и т. Д.

Один из способов проверить, есть ли в вашем коде утечка соединения, - заставить ds.getConnection () всегда открывать новое физическое соединение, а conn.close () - освободить соединение (если в вашем пуле соединений есть настройки для них). , Затем, если вы посмотрите на соединения на стороне MySQL, вы сможете выяснить, действительно ли в коде имеется утечка соединения.

1 голос
/ 08 ноября 2010

Проблема в том, что соединение не закрывается должным образом и застревает в «спящем» режиме

На самом деле это было только наполовину.

Проблема, с которой я столкнулся, заключалась в том, что каждое приложение определяло новое соединение с сервером базы данных. Поэтому каждый раз, когда я закрывал все соединения, приложение A создавало кучу новых соединений в соответствии с его конфигурационным файлом WEB.xml и работало счастливо. Приложение Б сделало бы то же самое. Проблема в том, что они являются независимыми пулами , которые пытаются получить доступ к серверному пределу. Думаю, это своего рода состояние гонки. Поэтому, когда приложение A завершило работу с соединениями, оно ожидает, чтобы использовать их снова, пока не истечет время ожидания, в то время как приложение B, которому необходимо подключение, теперь отказано в ресурсах, хотя приложение A завершило работу с и должно вернуться в пул. По истечении времени ожидания соединение освобождается, и B (или C и т. Д.) Могут снова его установить.

например. если предел равен 10 (предел профиля mySQL), и каждое приложение настроено на использование максимум 10, то будет 20 попыток подключения. Очевидно, это плохая ситуация.

Решение заключается в RTFM и поместите сведения о соединении в нужное место . Это делает совместную публикацию болью, но есть способы обойти это (например, ссылки на другие XML-файлы из контекста).

Просто чтобы быть в явном виде: я поместил детали подключения в WEB.xml для каждого приложения, и у меня возникла проблема.

0 голосов
/ 06 июля 2009

Я использую ту же конфигурацию, что и вы. Если соединение в mysql administrator (windows) показывает, что оно находится в спящем режиме, это означает только то, что оно находится в пуле, но не используется Я проверил это, запустив тестовую программу с несколькими потоками, делающими произвольные запросы к Mysql. если это поможет, вот моя конфигурация:

        defaultAutoCommit="false"
        defaultTransactionIsolation="REPEATABLE_READ"
        auth="Container"
        type="javax.sql.DataSource"
        logAbandoned="true" 
          removeAbandoned="true"
        removeAbandonedTimeout="300" 
        maxActive="-1"
        initialSize="15"
        maxIdle="10"
        maxWait="10000" 
        username="youruser"
        password="youruserpassword"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://yourhost/yourdatabase"/>
0 голосов
/ 10 сентября 2008

Одна вещь, которую @binil упустил, вы не закрываете набор результатов в случае исключения. В зависимости от реализации драйвера это может привести к тому, что соединение останется открытым. Переместите результат result.close () в блок finally.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...