2014-12-29 2 views
4

У меня возникла проблема, когда я получаю исключения tomcat после того, как я сброшу свою базу данных MySQL по просьбе пользователя через веб-приложение tomcat. Я попытался разбить это на настройку, проблему и мой анализ до сих пор, чтобы помочь любому, кто пытается это прочитать.Как сбросить пул соединений JDBC

Настройка

Сброс в основном состоит из вызова Баш скрипт из кода Java в:

  • Извлечение корня MySQL пароль пользователя
  • Загрузка в старой версии базы данных
  • Выполнение некоторых скриптов на нем
  • Восстановление всех паролей

Процедура, инициированная пользователем, обычно восстанавливает базу данных до предыдущего состояния, но также используется для импорта базы данных из другой системы. Как только все будет завершено, пользователь затем попытается получить доступ к другой части веб-приложения (т. Е. С тем же сеансом без выхода из системы/входа в систему), который выполняет запрос БД для получения некоторых данных.

Проблема

После БД запрашивается приложением TOMCAT, есть исключение:

Dec 29, 2014 3:49:50 PM ERROR BasicSecurityRealm:216 - 
ERROR: ----- SQLException ----- 

Dec 29, 2014 3:49:50 PM INFO BasicSecurityRealm:218 - Exceptioncom.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure 

The last packet successfully received from the server was 234,810 milliseconds ago. The last packet sent successfully to the server was 12 milliseconds ago. 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57) 
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
... 
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost. 
    at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2540) 
    at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2990) 

Даже если пользователь выходит из системы и обратно, я буду видеть это исключение. Если я обновляю страницу, четыре раза, страница будет загружаться немного больше каждый раз с некоторыми разными исключениями (все варианты выше - CommunicationException, вызванные «EOFException: Не удается прочитать ответ с сервера»). В последний раз все работает нормально.

Единственное, что я могу сделать, чтобы избежать этих исключений, - это перезагрузить tomcat. Я хотел бы избежать этого, потому что это будет означать, что текущий пользователь, который вошел в систему, потеряет свою сессию и должен ждать, пока tomcat перезагрузится, прежде чем они смогут вернуться в систему. Принуждение к входу/входу может быть приемлемым компромиссом , но это все равно не решает проблему.

Анализ

Из того, что я могу сказать, я думаю, что проблема связана с пулом соединений JDBC. Я использую источники JNDI данных для доступа к базе данных моего следующим образом:

server.xml:

<GlobalNamingResources> 
    <Resource name="jdbc/mydb" 
       auth="Container" 
       type="javax.sql.DataSource" 
       maxActive="30" maxIdle="30" maxWait="2147483647" 
       username="x" password="x" 
       driverClassName="com.mysql.jdbc.Driver" 
       url="jdbc:mysql://localhost:3306/mydb?autoReconnect=true"/> 

web.xml:

<!-- Data source definitions --> 
<resource-ref> 
    <res-ref-name>jdbc/mydb</res-ref-name> 
    <res-type>javax.sql.DataSource</res-type> 
    <res-auth>Container</res-auth> 
    <res-sharing-scope>Shareable</res-sharing-scope> 
</resource-ref> 

Java:

// Get connection to specified database 
    Context initCtx = new InitialContext(); 
    Context envCtx = (Context) initCtx.lookup("java:comp/env"); 
    DataSource ds = (DataSource) envCtx.lookup("jdbc/mydb"); 
    con = ds.getConnection(); 
    stmt = con.createStatement(); 
    rs = stmt.executeQuery("..."); 

Я считаю, что пул соединений содержит устаревшие/мертвые соединения. Всякий раз, когда я получаю соединение с ds.getConnection, он получает одно из этих старых соединений. Попытки использовать его не будут работать в первый раз и соединение будет сброшено (обратите внимание, что я использую autoReconnect=true, поэтому второй раз должен (и работает) работать). Однако пул содержит много (в моем случае, эмпирически 4 или 5) устаревших соединений, поэтому требуется некоторое время, прежде чем они будут правильно сброшены. Как только соединение сбрасывается, все ведет себя правильно.

Решения?

Поскольку я использую autoReconnect=true, я мог бы переструктурировать свой код, чтобы, если я получаю исключение при попытке запроса, я могу повторить запрос один раз. Если он снова не сработает, я бы знал, что есть действительно проблема. Если он пройдет, соединение успешно восстановится.

Проблема в том, что в коде есть запросы EVERYWHERE. Для повторного факторинга их потребуется много времени и тестов, которые я буду делать, если это необходимо, но хотелось бы избежать. Кроме того, если запрос терпит неудачу по другим причинам, он будет предпринят дважды, прежде чем сообщать об этом. Для длинных запросов это может привести к значительному задержке работы пользователя, но только в условиях ошибки.

Другим решением является принудительное сброс/повторное подключение всех подключений в пуле соединений. Я мог бы сделать это либо программно (т. Е. Из моего java-кода при завершении вызова скрипта bash), либо из сценария bash (например, с помощью какой-либо утилиты командной строки). Проблема в том, что я не знаю, как это сделать, или если это возможно.

Я нашел документацию по перехватчикам, но я не уверен, что это будет работать для сброса соединения. Я продолжу расследование.

Спасибо всем за ваше время и помощь!

+1

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

+0

@LuiggiMendoza Спасибо. Я думал об этом, но длинный рассказ короткий, это может быть не вариант. Сценарий, который запускается, не имеет учетных данных для изменения базы данных, поэтому мне нужно удалить пароль для пользователя root. Кроме того, новая загружаемая база данных может иметь разные пароли. – Trenin

+1

* Я думал об этом, но длинный рассказ короткий, это может быть не вариант *, а затем страдать в тишине u_u. Шутки в отдельности, вы не можете * перезагрузить * пул соединений с базой данных. Избегайте этого или выполняйте свой скрипт с пользователем, у которого достаточно сил для его выполнения. Вы могли бы создать другого пользователя, чтобы сделать это, или выключить приложения, а затем запустить их и установить для этого политики. –

ответ

3

Вы можете проверить соединение, прежде чем попасть из бассейна

По умолчанию Tomcat < 7 использует commond-dbcp для Tomcat> = 7 это jdbc-pool

В обоих случаях добавить следующие свойства конфигурации пула соединений:

validationQuery=<TEST SQL> 
testOnBorrow=true 
+0

Это похоже на правильный подход. Убедитесь, что вы проверяете свойство validationInterval пула. По умолчанию 30 секунд означает, что если соединение было протестировано за последние 30 секунд, запрос проверки не будет выполнен. В вашем случае использования вам кажется, что вам нужно сделать validationInterval равным 0. –

+0

@terma Звучит неплохо - testOnBorrow, похоже, работает. – Trenin

+0

@ AndyDufresne Кажется работать. Однако, если я знаю, что все соединения в пуле устарели, нет ли способа принудительно сбросить их все? Это будет более эффективным, чем проверка каждый раз. – Trenin

Смежные вопросы