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

Jetty 9 Hangs, QueuedThreadPool Growing Large

Недавно мы обновили наши серверы Jetty с версии 6.1.25 до 9.0.4. Они развертываются на Java 1.7.0_11 64-бит на сервере Windows 2008.

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

Во время моего первого обзора я подумал, что на что-то с https://bugs.eclipse.org/bugs/show_bug.cgi?id=357318. Однако эта проблема JVM была передана из Java 1.8_0XX в Java 1.7.0_06. Это привело меня к рассмотрению обработки потоков.

Думал, что это может быть связано с случаем 400617/410550 на сайте eclipse, хотя оно не похоже на запись, и этот случай, по-видимому, был устранен в Jetty 9.0.3.

Мониторинг приложения через JMX показывает, что количество потоков для потоков "qtp" продолжает расти с течением времени, и я не добился успеха в поиске разрешения. Конфигурация резьбы в настоящее время установлена ​​для:

threads.min=10
threads.max=200
threads.timeout=60000

Все потоки qtp обычно находятся в состоянии WAITING со следующей трассировкой стека:

Name: qtp1805176801-285
State: WAITING on [email protected]
Total blocked: 0  Total waited: 110

Stack trace: 
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
java.util.concurrent.Semaphore.acquire(Unknown Source)
org.eclipse.jetty.util.BlockingCallback.block(BlockingCallback.java:96)
org.eclipse.jetty.server.HttpConnection$Input.blockForContent(HttpConnection.java:457)
org.eclipse.jetty.server.HttpInput.consumeAll(HttpInput.java:282)
   - locked [email protected]
org.eclipse.jetty.server.HttpConnection.completed(HttpConnection.java:360)
org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:340)
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:224)
org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
java.lang.Thread.run(Unknown Source)

После более близкого просмотра это выглядит отличным от новейших потоков, которые имеют следующее состояние:

Name: qtp1805176801-734
State: TIMED_WAITING on java.u[email protected]77b83b6e
Total blocked: 5  Total waited: 478

Stack trace: 
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(Unknown Source)
org.eclipse.jetty.util.BlockingArrayQueue.poll(BlockingArrayQueue.java:390)
org.eclipse.jetty.util.thread.QueuedThreadPool.idleJobPoll(QueuedThreadPool.java:509)
org.eclipse.jetty.util.thread.QueuedThreadPool.access$700(QueuedThreadPool.java:48)
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:563)
java.lang.Thread.run(Unknown Source)

На основании соглашения об именах некоторые из потоков qtp очень старые (qtp1805176801-206), а некоторые - очень новые (qtp1805176801-6973). Мне интересно, что старые потоки не синхронизируются с 60-секундным таймером ожидания. Клиенты прикладных услуг во время деловых часов в США и в основном бездействуют в ранние утренние часы, когда я ожидаю, что почти весь бассейн будет очищен.

Надеясь, что кто-то сможет указать мне правильное направление с точки зрения того, как отслеживать эту проблему. Мой опыт работы с Jetty заставляет меня поверить, что их материал очень прочный, и большинство вопросов либо программны в нашей реализации (были там), либо связаны с JVM (это сделано). Также открыта для предложений, если вы думаете, что я могу преследовать красную селедку на Threads.

НОВАЯ ИНФОРМАЦИЯ: Отслеживание исключений немного дальше, это, по-видимому, вызвано тем, что вызовы GWT RPC выходят из строя во время ожидания ответа. Следующая трассировка стека показывает исключение в файле журнала, связанное с потоком, который находится в недопустимом состоянии. Используя это для просмотра и поиска других отчетов о проблемах взаимодействия Jetty/GWT.

2013-09-03 08:41:49.249:WARN:/webapp:qtp488328684-414: Exception while dispatching incoming RPC call
java.io.IOException: java.util.concurrent.TimeoutException: Idle timeout expired: 30015/30000 ms
    at org.eclipse.jetty.util.BlockingCallback.block(BlockingCallback.java:103)
    at org.eclipse.jetty.server.HttpConnection$Input.blockForContent(HttpConnection.java:457)
    at org.eclipse.jetty.server.HttpInput.read(HttpInput.java:130)
    at java.io.InputStream.read(Unknown Source)
    at com.google.gwt.user.server.rpc.RPCServletUtils.readContent(RPCServletUtils.java:175)
    at com.google.gwt.user.server.rpc.RPCServletUtils.readContentAsGwtRpc(RPCServletUtils.java:205)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.readContent(AbstractRemoteServiceServlet.java:182)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:239)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:755)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:698)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1506)
    at c.t.b.servlet.PipelineFilter.doFilter(PipelineFilter.java:56)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1494)
    at c.v.servlet.SetRequestEncoding.doFilter(SetRequestEncoding.java:27)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1494)
    at c.t.b.servlet.OutOfMemoryFilter.doFilter(OutOfMemoryFilter.java:39)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1486)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:138)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:564)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:213)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1094)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:432)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:175)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1028)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:258)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
    at org.eclipse.jetty.server.Server.handle(Server.java:445)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:267)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:224)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
    at java.lang.Thread.run(Unknown Source)
Caused by: 
java.util.concurrent.TimeoutException: Idle timeout expired: 30015/30000 ms
    at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:153)
    at org.eclipse.jetty.io.IdleTimeout$1.run(IdleTimeout.java:50)
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
4b9b3361

Ответ 1

Завершена публикация вопроса на веб-сайте Eclipse/Jetty. Следующая ссылка может использоваться для отслеживания любых постоянных исправлений решения.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=416477

Проблема связана с блокировкой Семафора в потоке QTP, который был запрограммирован во время запроса как часть вызова RPC GWT. Первоначальный запрос синхронизируется с таймаутом 30 секунд. Запрос истекает, пока он ждет по методу Semaphore.acquire. Как часть очистки запроса, HTTPConnection пытается выполнить .consumeAll по запросу, который снова пытается использовать Sempahore.acquire. На этот раз запрос не синхронизирован, и блокировка остается на месте до тех пор, пока поток не будет прерван.

Проблема, похоже, очень специфична для платформы, поскольку Jetty не смог воспроизвести проблему, и я не могу найти никаких других сообщений об этой проблеме. Кроме того, это происходит только в одной из наших производственных сред. Я предполагаю, что между кодом GWT RPC, Jetty и операционной системой происходит что-то. У нас есть небольшие обновления, запланированные для JDK, Jetty и GWT SDK.

Обход Первоначальная работа заключалась в том, чтобы вручную прерывать заблокированные потоки пару раз в день через консоль JMX. Наше долгосрочное решение заключалось в создании механизма очистки, который ищет эти заблокированные потоки и вызывает на них метод прерывания.

Ответ 2

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

Что касается стека, который вы вставили, то он неполный, поэтому количество угадывания на поведение чрезвычайно велико. Но, как говорится, эти две строки могут указывать на обычные операции, но без остальной части stacktrace мало что можно продолжить.

Кроме того, версии Java, которые вы используете 1.7.0_06 и 1.7.0_11, очень старые, и вы подвержены сотням исправлений ошибок.

Ответ 3

У меня то же самое с Jetty 9.2.3.v20140905 и Java (build 1.8.0_20-b26) 64 бит.

Обход. Установите monit http://mmonit.com/monit/

# monit.conf
check process jetty-service with pidfile "/opt/jetty-service/jetty.pid"
start program = "/usr/sbin/service jetty-service start" with timeout 30 seconds
stop program = "/usr/sbin/service jetty-service stop"
if totalmem is greater than 1268 MB for 10 cycles then restart
if 5 restarts within 5 cycles then timeout