Подтвердить что ты не робот

Пул соединений JDBC заканчивается из соединений, когда Context reload = "true" включен в Tomcat

Я разрабатываю веб-приложение Java EE в Eclipse Juno. Я настроил Tomcat для использования пула соединений JDBC (org.apache.tomcat.jdbc.pool) и базы данных PostgreSQL. Вот конфигурации в моем проекте META-INF/context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
    <!-- Configuration for the Tomcat JDBC Connection Pool -->
    <Resource name="jdbc/someDB"
        type="javax.sql.DataSource"
        auth="Container"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://localhost:5432/somedb"
        username="postgres"
        password="12345"
        maxActive="100"
        minIdle="10"
        initialSize="10"
        validationQuery="SELECT 1"
        validationInterval="30000"
        removeAbandoned="true"
        removeAbandonedTimeout="60"
        abandonWhenPercentageFull="50" />
</Context>

Мое приложение развернуто в Tomcat с использованием Eclipse, а в Tomcat context.xml атрибут reloadable установлен на "true", чтобы автоматически перезагрузить веб-приложение, если обнаружено изменение:

<Context reloadable="true">

Я заметил, что каждый раз, когда вышеупомянутая автоматическая перезагрузка происходит, еще 10 соединений с PostgreSQL db зарезервированы (потому что в webapp context.xml initialSize = "10" ). Итак, после 10 изменений вызывается PSQLException:

org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
...

Если я вручную перезагружаю Tomcat - все в порядке и зарезервировано только 10 подключений.

Знает ли кто-нибудь об этой проблеме, так что можно было бы разработать с перезагружаемым набором "true" и не приводить к объединению большего количества подключений при каждом перезагрузке контекста?

Поблагодарили бы за любую помощь.

P.S. Apache Tomcat Version 7.0.32

4b9b3361

Ответ 1

РЕШЕНИЕ (tl; dr)

Чтобы решить эту проблему, добавьте атрибут closeMethod (зарегистрированный здесь) со значением "закрыть" к элементу Resource в файле context.xml.

Здесь корректное содержимое моего /META -INF/context.xml файла:

<Context>
    <!-- Configuration for the Tomcat JDBC Connection Pool -->
    <Resource name="jdbc/someDB"
        type="javax.sql.DataSource"
        auth="Container"
        factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        driverClassName="org.postgresql.Driver"
        url="jdbc:postgresql://localhost:5432/somedb"
        username="postgres"
        password="12345"
        maxActive="100"
        minIdle="10"
        initialSize="10"
        validationQuery="SELECT 1"
        validationInterval="30000"
        removeAbandoned="true"
        removeAbandonedTimeout="60"
        abandonWhenPercentageFull="50"
        closeMethod="close" />
</Context>

Обратите внимание на атрибут closeMethod. Я тестировал его, и теперь количество подключений хранится СТРОГО, как определено в файле context.xml!

Примечание
Существует один момент (связанный с JNDI), о котором можно позаботиться. Для полного описания см. UPDATE 3.


Длинный ответ

Хорошо, я нашел вышеупомянутое решение благодаря Apache Tomcat committor Константин Колинко. Я сообщил эту проблему в качестве ошибки Apache Tomcat в ASF Bugzilla и оказалось, что это не ошибка (см. UPDATE 1).

=== ОБНОВЛЕНИЕ 1 (2012-12-03) aka "Новая надежда" ===

Ну, это все еще оказалось ошибкой. Марк Томас, менеджер выпуска Apache Tomcat 7, подтвердил, что (цитата):

"Это ошибка утечки памяти в jdbc-пуле. Экземпляры PoolCleaner сохраняя ссылки на ConnectionPool, не позволяя ему GC'd.
...
Это было исправлено в багажнике и 7.0.x и будет включено в 7.0.34 и далее."

Итак, если у вас есть более старая версия Tomcat (менее 7.0.34), используйте вышеупомянутое решение, иначе , начиная с Apache Tomcat версии 7.0.34, не должно быть проблем, подобных тем, которые я описал. (см. ОБНОВЛЕНИЕ 2)

=== ОБНОВЛЕНИЕ 2 (2014-01-13) aka "Проблема наносит ответный удар" ===

Кажется, что проблема, первоначально описанная в мой отчет об ошибке, по-прежнему присутствует даже для последней версии Apache Tomcat версии 7.0.50 и я также воспроизвел его с Tomcat 7.0.47 (спасибо Миклош Криван, указав его). Хотя теперь Tomcat иногда удается закрыть дополнительные соединения после перезагрузки, а иногда количество подключений увеличивается после одной перезагрузки, а затем остается стабильным, но в конечном итоге это поведение по-прежнему не является надежным.

Я все еще мог воспроизвести изначально описанную проблему (хотя это опять-таки не так просто: это может быть связано с частотой последовательных перезагрузок). Кажется, что это просто вопрос времени, то есть если Tomcat имеет достаточно времени после перезагрузки, он управляет пулом подключений более или менее, как должен. Как отметил Марк Томас в своем комментарий (цитата): "Согласно документам для closeMethod, этот метод существует только для ускорения освобождения ресурсов которые в противном случае были бы освобождены ГК". (конец цитаты), и похоже, что скорость является определяющим фактором.

При использовании решения, представленного Константином Колинко (для использования closeMethod = "close" ), все работает нормально, а количество зарезервированных соединений хранится СТРОГО, как определено в файле context.xml. Таким образом, кажется, что использование closeMethod = "close" является ТОЛЬКО истинным способом (на данный момент), чтобы избежать завершения соединений после перезагрузки контекста.

=== ОБНОВЛЕНИЕ 3 (2014-01-13) aka "Возврат диспетчера выпуска Tomcat" ===

Тайна поведения, описанного в UPDATE 2, решена. Более подробная информация была очищена теперь после того, как я получил ответ от Марка Тома (менеджер выпуска Tomcat). Надеюсь, это последнее обновление. Таким образом, ошибка была действительно исправлена ​​, как было упомянуто в ОБНОВЛЕНИИ 1. Я размещаю существенную часть от Марка здесь в качестве цитаты (акцент мой):

Фактическая утечка памяти, обнаруженная при исследовании этой ошибки, была исправлено в 7.0.34 и далее в соответствии с комментариями от # 4 до #.

Вопрос о том, что соединения не закрываются при перезагрузке, является результатом спецификация J2EE для ресурсов JNDI и эта часть ошибки поэтому отчет недействителен. Я восстанавливаю состояние этой ошибки до исправлено с учетом того, что утечка памяти, которая действительно существует, была исправлена.

Чтобы узнать, почему не удалось сразу закрыть соединение после перезагрузка недопустима, спецификация J2EE не предоставляет механизма для контейнер, чтобы сообщить ресурс, он больше не требуется. Поэтому все контейнер может делать это четкие ссылки на ресурс и ждать сбор мусора (который вызовет закрытие пула и связанные соединения). Сбор мусора происходит порой JVM настолько , поэтому для неопределенного периода времени требуется соединения будут закрыты после перезагрузки контекста как мусор сбор может не произойти в течение некоторого времени.

Tomcat добавил специфичный для Tomcat JNDI-атрибут closeMethod, который может использоваться для запуска явного закрытия ресурса JNDI, когда контекст остановлен. Если ожидающий GC для очистки ресурсов не допустимо, то просто используйте этот параметр. Tomcat не использует этот по умолчанию в качестве могут иметь неожиданные и нежелательные побочные эффекты для некоторые ресурсы JNDI.

Если вы хотите увидеть стандартный механизм, предоставляемый для указания JNDI ресурсов, которые им больше не нужны, тогда вам нужно лоббировать Экспертная группа J2EE.

Заключение

Просто используйте решение, представленное в начале этого сообщения (но, на всякий случай, имейте в виду связанную с JNDI проблему, которая теоретически может возникнуть из-за ее использования).


Альтернативное решение

Майкл Осипов предложил использовать CloseableResourceListener, что предотвращает утечку памяти вызванных оставленными открытыми ресурсами во время развертывания веб-приложений. Поэтому вы также можете попробовать.


ОТКАЗ
Псевдонимы для UPDATES были вдохновлены серией Star Wars. Все права принадлежат их соответствующим владельцам.