Nous avons récemment mis à niveau nos serveurs Jetty de la version 6.1.25 à la version 9.0.4. Ils sont déployés sur Java 1.7.0_11 64 bits sur un serveur Windows 2008.
Hormis les modifications de configuration requises pour Jetty (start.ini - very Nice), nous avons conservé les indicateurs JVM identiques. Six jours après le déploiement dans l'environnement de production, le serveur ne répondait plus aux requêtes HTTP. Le traitement interne des «pulsations» a continué de s'exécuter normalement, mais il ne répondait pas aux demandes externes. Le service a été redémarré et 6 jours plus tard, il est à nouveau devenu insensible.
Lors de mon examen initial, je pensais être sur quelque chose avec https://bugs.Eclipse.org/bugs/show_bug.cgi?id=357318 . Toutefois, ce problème de machine virtuelle Java a été rétroporté de Java 1.8_0XX à Java 1.7.0_06. Cela m'a amené à passer en revue le traitement des threads.
Je pensais que cela pourrait être lié à l'affaire 400617/410550 sur le site Eclipse, bien qu'elle ne se présente pas exactement de la même manière que dans l'article précédent et que l'affaire avait apparemment été résolue dans Jetty 9.0.3.
Le fait de surveiller l'application via JMX montre que le nombre de threads pour les threads 'qtp' continue de croître avec le temps et que la recherche d'une résolution a échoué. La configuration des threads est actuellement définie pour:
threads.min=10
threads.max=200
threads.timeout=60000
Tous les threads qtp sont généralement à l'état WAITING avec le suivi de pile suivant:
Name: qtp1805176801-285
State: WAITING on Java.util.concurrent.Semaphore$NonfairSync@4bf4a3b0
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 org.Eclipse.jetty.util.ArrayQueue@3273ba91
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)
Après un examen plus approfondi, cela semble être différent des threads les plus récents ayant l'état suivant:
Name: qtp1805176801-734
State: TIMED_WAITING on Java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@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)
Selon la convention de dénomination, certains des threads qtp sont très anciens (qtp1805176801-206), tandis que d'autres sont très récents (qtp1805176801-6973). Je trouve intéressant que les threads plus anciens ne soient pas hors délai en fonction du délai d'inactivité de 60 secondes. L'application dessert les clients pendant les heures ouvrables aux États-Unis et est en grande partie inactive tôt le matin, heure à laquelle je m'attendrais à ce que la quasi-totalité de la piscine soit nettoyée.
En espérant que quelqu'un pourra me guider dans la bonne direction pour trouver une solution à ce problème. Mon expérience avec Jetty me porte à croire que leur contenu est très solide et que la plupart des problèmes sont soit programmatiques dans notre mise en œuvre (déjà passés là), soit liés à la JVM (fait cela). Également ouvert aux suggestions si vous pensez que je pourrais être en train de chasser un hareng rouge sur les Threads.
NOUVELLES INFORMATIONS: En traçant les exceptions un peu plus loin, cela semble être dû au fait que les appels RPC GWT arrivent à expiration en attendant une réponse. La trace de pile suivante montre une exception dans le fichier journal liée à un thread qui est dans un état non valide. En l'utilisant, vous pouvez consulter et rechercher d'autres rapports sur les problèmes d'interaction 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)
Nous avons fini par poster la question sur le site Web Eclipse/Jetty. Le lien suivant peut être utilisé pour suivre toute solution permanente à la solution.
https://bugs.Eclipse.org/bugs/show_bug.cgi?id=416477
Le problème concerne le verrouillage du sémaphore sur un thread QTP qui a expiré lors de la requête dans le cadre d'un appel RPC GWT. La demande initiale est chronométrée, avec un délai d'attente de 30 secondes. La demande expire pendant qu'elle attend la méthode Semaphore.acquire. Dans le cadre du nettoyage de la demande, HTTPConnection tente d'exécuter .consumeAll sur la demande, qui tente à nouveau un Sempahore.acquire. Cette fois, la demande n'est pas chronométrée et le verrou reste en place jusqu'à ce que le thread soit interrompu.
Le problème semble être très spécifique à la plate-forme car Jetty n’a pas été en mesure de le reproduire et je n’ai pas pu trouver d’autres rapports sur le problème. De plus, cela ne se produit que dans l'un de nos environnements de production. À mon avis, il se passe quelque chose entre le code RPC GWT, la jetée et le système d’exploitation. Nous avons prévu des mises à niveau mineures pour le JDK, Jetty et le SDK GWT.
Solution de contournement Le travail initial consistait à interrompre manuellement les threads verrouillés plusieurs fois par jour via la console JMX. Notre solution à plus long terme consistait à créer un mécanisme de nettoyage qui recherche ces threads verrouillés et appelle la méthode d'interruption.
QueuedThreadPool est un pool de threads partagé. Les threads qu'il contient seront réutilisés pour d'autres traitements. Oui, la poursuite du pool de threads, en supposant que les threads seront nettoyés, est un hareng rouge. Ces fils vont tomber de la piscine, lentement, sur une longue période de temps (pensez heures). C’est une décision de performance dans le pool de threads (la création est coûteuse, faites-le le moins souvent possible).
Quant au stacktrace que vous avez collé, il est incomplet, de sorte que le nombre de devinettes sur le comportement est extrêmement élevé. Mais ceci étant dit, ces 2 lignes peuvent indiquent des opérations normales, mais sans le reste de la pile, il reste peu à faire.
De plus, les versions de Java que vous utilisez 1.7.0_06 et 1.7.0_11 sont très anciennes et vous êtes sujet à des centaines de corrections de bugs.
J'ai la même chose avec Jetty 9.2.3.v20140905 et Java (build 1.8.0_20-b26) 64 bits.
Solution de contournement . Installez 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