Мое Java-приложение под управлением Tomcat (7.0.28) периодически перестает отвечать на запросы. Я надеюсь на некоторые предложения возможных преступников (синхронизация?), А также, возможно, некоторые рекомендуемые инструменты для сбора дополнительной информации о том, что происходит во время сбоя. Некоторые факты, которые я накопил:
-
Когда веб-приложение зависнет, tomcat продолжает подавать потоки запросов в приложение, но приложение не освобождает их. Пул потоков заполняется до максимума (в настоящее время 250), а затем последующие запросы немедленно терпят неудачу. Во время нормальной работы не более 2 или 3 активных потоков.
-
При возникновении проблемы нет ошибок или исключений любого вида, зарегистрированных в любом из журналов tomcat или веб-приложений.
-
Выполнение "Стоп", а затем "Начать" в нашем приложении через веб-приложение управления tomcat немедленно устраняет эту проблему (до сегодняшнего дня).
-
В последнее время частота была два или три раза в день, хотя сегодня было намного хуже, вероятно, 20 раз, и иногда не возвращалось к жизни немедленно.
-
Проблема возникает только в рабочее время
-
Проблема не возникает в нашей промежуточной системе
-
При возникновении проблемы использование процессора и памяти на сервере остается плоским (и довольно низким). Tomcat сообщает о большом количестве свободной памяти.
-
Tomcat продолжает реагировать, когда возникает проблема. Веб-приложение управления работает отлично, и tomcat продолжает отправлять запросы в наше приложение, пока не будут заполнены все потоки в пуле.
-
Наш сервер баз данных остается отзывчивым, когда возникает проблема. Мы используем инфраструктуру Spring для доступа к данным и инъекции.
-
Проблема обычно возникает, когда использование является высоким, но никогда не бывает необычно высокого всплеска в использовании.
-
История проблем: что-то подобное произошло около полутора лет назад. После многих изменений в конфигурации сервера и кода проблема исчезла примерно до месяца назад. В течение последних нескольких недель это происходило гораздо чаще, в среднем 2 или 3 раза в день, иногда несколько раз подряд.
-
Сегодня я определил некоторый код сервера, который, возможно, не был потокобезопасным, и я установил исправление для него, но проблема все еще происходит (хотя и реже). Является ли это той проблемой, которую может вызвать не-потоковый код?
ОБНОВЛЕНИЕ:. С несколькими сообщениями, в которых говорится об исчерпании пула подключений к базе данных, я сделал несколько поисков в этом направлении и нашел этот другой qaru.site/info/247356/..., который объясняет почти все проблемы, которые я испытываю.
По-видимому, значения по умолчанию для соединений maxActive и maxIdle в реализации Apache BasicDataSource равны 8. Кроме того, maxWait установлен на -1, поэтому, когда пул исчерпан и появляется новый запрос на соединение, он будет ждать вечно без регистрации каких-либо исключений. Я все еще жду, когда эта проблема повторится, и выполните jstack dump на JVM, чтобы я мог анализировать эту информацию, но похоже, что это проблема. Единственное, что не объясняет, почему приложение иногда не восстанавливается после этой проблемы. Я полагаю, что запросы просто накапливаются иногда, и как только он позади, он никогда не сможет догнать.
ОБНОВЛЕНИЕ II: Я столкнулся с jstack во время сбоя и обнаружил около 250 (максимальных потоков) следующего содержания:
"http-nio-443-exec-294" daemon prio=10 tid=0x00002aaabd4ed800 nid=0x5a5d in Object.wait() [0x00000000579e2000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118)
- locked <0x0000000743116b30> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:573)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:674)
at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:718)
Для моего неподготовленного глаза это выглядит довольно убедительно. Похоже, что пул подключений к базе данных попал в кепку. Я сконфигурировал maxWait из трех секунд без изменения maxActive и maxIdle, чтобы убедиться, что мы начинаем видеть исключения, регистрируемые при заполнении пула. Затем я установлю эти значения на что-то подходящее и контролирую.
ОБНОВЛЕНИЕ III: После настройки maxWait я начал видеть их в своих журналах, как и ожидалось:
org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:114)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
Я установил maxActive на -1 (бесконечный) и maxIdle на 10. Я буду контролировать некоторое время, но я предполагаю, что это конец проблемы.