Apache + Tomcat ayant des problèmes de communication. Messages d'erreur peu clairs. Faire tomber des sites Web hébergés sous Tomcat

22

Configuration:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache transfère les demandes à l'aide d'AJP.

Problème:
après un certain laps de temps (pas de constante du tout, cela peut prendre entre une heure ou deux, ou un ou plusieurs jours) Tomcat va tomber. Soit il cesse de répondre, soit il met en place le «service temporairement indisponible» générique.

Diagnostic:
Il y a deux serveurs avec la même configuration. L'un héberge un site Web à plus fort trafic (plusieurs demandes par seconde), l'autre un site à faible trafic (une poignée de demandes toutes les quelques minutes). Les deux sites Web sont des bases de code complètement différentes, mais ils présentent des problèmes similaires.

Sur le premier serveur, lorsque le problème se produit, tous les threads commencent lentement à être utilisés jusqu'à ce qu'il atteigne la limite (MaxThreads 200). À ce stade, le serveur ne répond plus (et propose la page de service indisponible après une longue période).

Sur le deuxième serveur, lorsque le problème se produit, les demandes prennent beaucoup de temps et lorsqu'elles sont terminées, tout ce que vous voyez est la page de service indisponible.

Mis à part la mention du problème MaxThreads, les journaux Tomcat n'indiquent aucun problème spécifique pouvant être à l'origine de ce problème.

Cependant, dans les journaux Apache, nous voyons des messages aléatoires faisant référence à AJP. Voici un échantillon de message aléatoire que nous voyons (dans aucun ordre spécifique):

[error] (70007)The timeout specified has expired: ajp_ilink_receive() can't receive header
[error] (104)Connection reset by peer: ajp_ilink_receive() can't receive header
[error] proxy: AJP: disabled connection for (localhost)
[error] ajp_read_header: ajp_ilink_receive failed
[error] (120006)APR does not understand this error code: proxy: read response failed from 127.0.0.1:8009 (localhost)
[error] ap_proxy_connect_backend disabling worker for (localhost)

L'autre chose étrange que nous avons remarquée sur le serveur à trafic plus élevé est que juste avant le début du problème, les requêtes de base de données prennent beaucoup plus de temps qu'auparavant (2000-5000 ms contre normalement 5-50ms). Cela ne dure que 2 à 4 secondes avant l'apparition du message MaxThreads. Je suppose que cela est dû au fait que le serveur traite soudainement trop de données / trafic / threads.

Informations générales:
Ces deux serveurs fonctionnaient sans problème depuis un certain temps. Les systèmes ont en fait été configurés chacun à l'aide de deux cartes réseau pendant cette période. Ils ont séparé le trafic interne et externe. Après une mise à niveau du réseau, nous avons déplacé ces serveurs vers des cartes réseau uniques (cela nous a été recommandé pour des raisons de sécurité / simplicité). Après ce changement, les serveurs ont commencé à avoir ces problèmes.

Résolution:
La solution évidente serait de revenir à une configuration de deux cartes réseau. Les problèmes avec cela sont que cela entraînerait des complications avec la configuration du réseau, et cela semble ignorer le problème. Nous préférons essayer de le faire fonctionner sur une seule configuration de carte réseau.

La recherche sur les différents messages d'erreur n'a fourni rien d'utile (que ce soit d'anciennes solutions ou sans rapport avec notre problème).

Nous avons essayé d'ajuster les différents délais d'attente, mais cela a simplement fait fonctionner le serveur un peu plus longtemps avant de mourir.

Nous ne savons pas où chercher pour diagnostiquer davantage le problème. Nous essayons toujours de comprendre quel pourrait être le problème:

1) La configuration avec AJP et Tomcat est incorrecte ou obsolète (c.-à-d. Des bogues connus?)
2) La configuration du réseau (deux cartes réseau contre une carte réseau) cause de la confusion ou des problèmes de débit.
3) Les sites Web eux-mêmes (il n'y a pas de code commun, pas de plates-formes utilisées, juste du code Java de base avec des servlets et JSP)

Mise à jour 1:
suite aux conseils utiles de David Pashley, j'ai effectué un vidage de trace / thread de pile pendant le problème. Ce que j'ai trouvé, c'est que les 200 threads étaient dans l'un des états suivants:

"TP-Processor200" daemon prio=1 tid=0x73a4dbf0 nid=0x70dd waiting for monitor entry [0x6d3ef000..0x6d3efeb0]
at  oracle.jdbc.pool.OracleConnectionCacheImpl.getActiveSize(OracleConnectionCacheImpl.java:988)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]

"TP-Processor3" daemon prio=1 tid=0x08f142a8 nid=0x652a waiting for monitor entry [0x75c7d000..0x75c7ddb0]
at oracle.jdbc.pool.OracleConnectionCacheImpl.getConnection(OracleConnectionCacheImpl.java:268)
- waiting to lock <0x7e3455a0> (a oracle.jdbc.pool.OracleConnectionCacheImpl)
[further stack trace removed for brevity]

Curieusement, un seul thread sur les 200 threads était dans cet état:

"TP-Processor2" daemon prio=1 tid=0x08f135a8 nid=0x6529 runnable [0x75cfe000..0x75cfef30]
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at oracle.net.ns.Packet.receive(Unknown Source)
at oracle.net.ns.DataPacket.receive(Unknown Source)
at oracle.net.ns.NetInputStream.getNextPacket(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
at oracle.net.ns.NetInputStream.read(Unknown Source)
[further stack trace removed for brevity]

Il se peut que le pilote Oracle de ce thread force tous les autres threads à attendre qu'il se termine. Pour une raison quelconque, il doit être bloqué dans cet état de lecture (le serveur ne récupère jamais seul, il nécessite un redémarrage).

Cela suggère qu'il doit être lié soit au réseau entre le serveur et la base de données, soit à la base de données elle-même. Nous poursuivons nos efforts de diagnostic, mais tout conseil serait utile.

Jordy Boom
la source
Tout d'abord, c'est une question écrite de façon impressionnante. Un travail fantastique sur les détails! Deuxièmement, utilisez-vous proxy_ajp ou mod_jk pour connecter les serveurs Apache et Tomcat?
Ophidian le
J'utilise proxy_ajp pour connecter les deux.
Jordy Boom
Faites des tests de résistance en utilisant Siege , joedog.org/siege-home .
paalfe

Réponses:

9

Il s'avère que cette version (classes12 - assez ancienne) du pilote Oracle contenait divers bogues qui provoquaient un blocage (comme vu dans l'état TP-Processor2 cité ci-dessus). Il n'est devenu actif que lorsque nous sommes passés au nouvel environnement. La mise à niveau vers la dernière version (ojdbc14) a résolu le problème sur le serveur principal.

Jordy Boom
la source
Cela m'a conduit à ma bonne solution: j'avais un verrou dans une ligne DB ... et je n'ai jamais eu d'exception dans l'App-Server
cljk
6

D'après la description, je suggère que le problème peut être dû au fait que les requêtes de base de données prennent trop de temps. Si les requêtes prennent plus de temps, la demande prendra plus de temps et donc vous en aurez plusieurs en même temps. Comme vous le voyez, vous manquez de threads tomcat. Lorsque vous résolvez le problème avec la base de données, vous devriez être d'accord.

  • Obtenez une trace de pile, soit en utilisant jstack, soit en utilisant kill -3 $ process_id. Voyez ce que font vos fils quand il meurt. S'ils attendent tous sur la base de données, c'est un bon pointeur vers ma théorie. Ils attendent peut-être tous une serrure.
  • Installez LambdaProbe. C'est précieux pour savoir ce que fait votre matou.
  • Améliorez votre tomcat. 5.5.8 est incroyablement vieux. Je pense qu'ils sont maintenant sur 5.5.27.
David Pashley
la source
David, j'ai mis à jour la question (voir la mise à jour 1) avec de nouvelles conclusions basées sur votre suggestion de trace de vidage / pile de thread.
Jordy Boom
Je suggère que votre pool de connexions à la base de données est trop petit par rapport à votre valeur de connexion tomcat max. Il semble que la plupart des threads attendent d'obtenir une connexion à la base de données.
David Pashley
La seule raison pour laquelle il existe de nombreux threads est que les threads normalement utilisés sont laissés en attente de ce thread essayant de lire à partir du socket. Le nombre de connexions DB utilisées à tout moment varie entre 1 et 3. Il n'y en a jamais besoin de plus que cela.
Jordy Boom
5

Ajoutez connectionTimeout et keepAliveTimeout à votre connecteur AJP trouvé dans /etc/tomcat7/server.xml.

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" 
           connectionTimeout="10000" keepAliveTimeout="10000" />

Informations sur le connecteur AJP sur https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html

  • connectionTimeout = Le nombre de millisecondes pendant lequel ce connecteur attendra, après avoir accepté une connexion, que la ligne URI de demande soit présentée. La valeur par défaut pour les connecteurs de protocole AJP est -1 (c'est-à-dire infinie).

  • keepAliveTimeout = Le nombre de millisecondes pendant lequel ce connecteur attendra une autre demande AJP avant de fermer la connexion. La valeur par défaut consiste à utiliser la valeur qui a été définie pour l'attribut connectionTimeout.

Si les valeurs connectionTimeout et keepAliveTimeout ne sont pas définies, les connexions AJP seront maintenues vivantes pendant une durée infinie. Causant de nombreux threads, le nombre maximum de threads par défaut est de 200.

Je recommande d'installer psi-probe - un gestionnaire et un moniteur avancés pour Apache Tomcat, issu de Lambda Probe. https://code.google.com/p/psi-probe/

paalfe
la source
4

En raison du fonctionnement d'AJP, les connexions persistantes entre apache (à l'aide de mod_proxy_ajp ou mod_jk) ne peuvent être fermées en toute sécurité que par le client . Dans ce cas, le client est le travailleur apache qui s'ouvre, puis détient une connexion à tomcat pour la durée de vie du processus de travail .

En raison de ce comportement, vous ne pouvez pas avoir plus de travailleurs Apache que de threads de travail Tomcat. Cela entraînera l'échec de la connexion des autres utilisateurs http à tomcat (car la file d'attente d'acceptation est pleine) et marquera votre back-end comme étant DOWN!

Dave Cheney
la source
1
Désolé pour le commentaire après toutes ces années, mais cela ne pourrait-il pas être garanti en définissant le max-flag dans la configuration ProxyPass sur le nombre de MaxThreads du conteneur de servlet?
Horst Gutmann
2

J'ai eu de meilleurs résultats avec mod_proxy au lieu de mod_ajp en termes de stabilité, alors essayez cette solution. Il est non invasif - au mieux, il résoudra le problème et au pire, il exclura mod_ajp.

À part cela, il semble que vos Tomcats ne répondent plus et que tous les threads de demande sont bloqués. Demandez à votre équipe de développement de regarder ce qui se passe - prendre un vidage de thread et le leur fournir sera utile.

Robert Munteanu
la source
J'avais l'impression que mod_proxy a quelques problèmes d'évolutivité, bien qu'il soit plus facile de le connecter. Il semble que la fondation Apache recommande mod_jk ( wiki.apache.org/tomcat/FAQ/Connectors#Q2 )
Ophidian
Il ne fournit pas de sesssion collante, c'est vrai. Mais à part ça, je n'ai jamais eu de problème avec ça.
Robert Munteanu
1

La première chose à laquelle je pense quand j'entends qu'un serveur fonctionne pendant un certain temps, ralentit soudainement et commence ensuite à avoir des échecs de service, c'est qu'il manque de RAM et de swashing. Je ne sais pas si les échecs AJP que vous voyez pourraient être la conséquence de délais d'attente, mais cela ne semble pas complètement déraisonnable; cependant, je ne vois pas de manière évidente de se connecter à la carte réseau. Dans tous les cas, je vous recommande de vous faire une idée de ce qui se passe avec votre utilisation de la mémoire lorsque ces événements se produisent.

Si vous manquez de RAM, vous devrez peut-être baisser votre Apache MaxClientset augmenter votre ListenBacklog.

Soit dit en passant, merci d'avoir rendu votre question si bien organisée et complète.

le chaos
la source
Lorsque j'observe «top» pendant que cela se produit, l'utilisation de la mémoire reste assez cohérente. Au moins, il n'y a pas de pointes. Il n'y a qu'un bref instant d'utilisation élevée du processeur.
Jordy Boom
1

J'ai eu des erreurs de journal similaires dans l'environnement Redhat avec proxy_ajp et Tomcat. Résolu en mettant à jour le package httpd:

yum update httpd

de:

  • httpd-devel-2.2.3-43.el5_5.3.x86_64
  • httpd-2.2.3-43.el5_5.3.x86_64

à:

  • httpd-2.2.3-45.el5_6.3.x86_64
  • httpd-devel-2.2.3-45.el5_6.3.x86_64

Puis redémarré apache, puis redémarré Tomcat.

Cela m'a arrangé!

Basse
la source