Apache + Tomcat hat Kommunikationsprobleme. Unklare Fehlermeldungen. Herunterfahren von Websites, die unter Tomcat gehostet werden

22

Setup:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache leitet Anfragen über AJP weiter.

Problem:
Nach einer bestimmten Zeitspanne (keine Konstante, kann zwischen einer oder zwei Stunden oder einem oder mehreren Tagen liegen) fällt Tomcat aus. Entweder reagiert es nicht mehr oder es wird der allgemeine "Dienst vorübergehend nicht verfügbar" angezeigt.

Diagnose:
Es gibt zwei Server mit demselben Setup. Eine Website hat einen höheren Traffic (mehrere Anfragen pro Sekunde), die andere einen niedrigen Traffic (eine Handvoll Anfragen alle paar Minuten). Beide Websites sind völlig unterschiedliche Codebasen, weisen jedoch ähnliche Probleme auf.

Wenn auf dem ersten Server das Problem auftritt, werden alle Threads langsam ausgelastet, bis das Limit erreicht ist (MaxThreads 200). Zu diesem Zeitpunkt reagiert der Server nicht mehr (und ruft nach einer langen Zeit die Seite auf, auf der der Dienst nicht verfügbar ist).

Wenn auf dem zweiten Server das Problem auftritt, dauern die Anforderungen sehr lange. Wenn sie erledigt sind, wird nur die Seite "Dienst nicht verfügbar" angezeigt.

Abgesehen von der Erwähnung des MaxThreads-Problems weisen die Tomcat-Protokolle nicht auf bestimmte Probleme hin, die dies verursachen könnten.

In den Apache-Protokollen sehen wir jedoch zufällige Nachrichten, die sich auf AJP beziehen. Hier ist ein Beispiel einer zufälligen Nachricht, die wir sehen (in keiner bestimmten Reihenfolge):

[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)

Die andere seltsame Sache, die wir auf dem Server mit höherem Datenverkehr bemerkt haben, ist, dass Datenbankabfragen viel länger dauern, bevor das Problem auftritt (2000-5000 ms im Vergleich zu normalerweise 5-50 ms). Dies dauert nur 2-4 Sekunden, bevor die MaxThreads-Meldung angezeigt wird. Ich gehe davon aus, dass dies darauf zurückzuführen ist, dass der Server plötzlich zu viele Daten / Datenverkehr / Threads verarbeitet.

Hintergrundinformation:
Diese beiden Server liefen schon länger ohne Probleme. Die Systeme wurden während dieser Zeit jeweils mit zwei Netzwerkkarten eingerichtet. Sie trennten internen und externen Verkehr. Nach einem Netzwerk-Upgrade haben wir diese Server auf einzelne Netzwerkkarten verschoben (dies wurde uns aus Gründen der Sicherheit und Einfachheit empfohlen). Nach dieser Änderung hatten die Server diese Probleme.

Lösung:
Die naheliegende Lösung besteht darin, wieder zwei Netzwerkkarten einzurichten. Die Probleme dabei sind, dass es einige Komplikationen bei der Netzwerkeinrichtung verursachen würde, und es scheint, als würde das Problem ignoriert. Wir würden es vorziehen, zu versuchen, es auf einem einzelnen NIC-Setup zum Laufen zu bringen.

Das Durchsuchen der verschiedenen Fehlermeldungen brachte nichts Nützliches (entweder alte Lösungen oder nichts mit unserem Problem zu tun).

Wir haben versucht, die verschiedenen Zeitüberschreitungen anzupassen, aber dies hat dazu geführt, dass der Server vor dem Tod etwas länger lief.

Wir sind nicht sicher, wo wir das Problem weiter diagnostizieren sollen. Wir fassen immer noch nach Strohhalmen, an denen das Problem liegen könnte:

1) Das Setup mit AJP und Tomcat ist falsch oder veraltet (dh bekannte Fehler?)
2) Das Netzwerk-Setup (zwei Netzwerkkarten gegen eine Netzwerkkarte) verursacht Verwirrung oder Durchsatzprobleme.
3) Die Websites selbst (es gibt keinen gemeinsamen Code, es werden keine Plattformen verwendet, nur grundlegender Java-Code mit Servlets und JSP)

Update 1:
Nach dem hilfreichen Rat von David Pashley habe ich während des Problems einen Stack-Trace / Thread-Dump durchgeführt. Was ich fand, war, dass sich alle 200 Threads in einem der folgenden Zustände befanden:

"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]

Seltsamerweise befand sich nur ein Thread von allen 200 Threads in diesem Zustand:

"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]

Möglicherweise zwingt der Oracle-Treiber in diesem Thread alle anderen Threads, auf den Abschluss zu warten. Aus irgendeinem Grund muss es in diesem Lesezustand hängen bleiben (der Server stellt sich niemals von selbst wieder her, es ist ein Neustart erforderlich).

Dies deutet darauf hin, dass es sich entweder um ein Netzwerk zwischen dem Server und der Datenbank oder um die Datenbank selbst handeln muss. Wir setzen unsere Diagnosemaßnahmen fort, aber alle Tipps wären hilfreich.

Jordy Boom
quelle
Zunächst einmal ist dies eine großartige schriftliche Frage. Fantastische Arbeit im Detail! Zweitens verwenden Sie proxy_ajp oder mod_jk, um die Apache- und Tomcat-Server zu verbinden?
Ophidian
Ich benutze proxy_ajp, um die beiden zu verbinden.
Jordy Boom
Mache Stresstests mit Belagerung, joedog.org/siege-home .
Paalfe

Antworten:

9

Es stellt sich heraus, dass diese Version (Klassen 12 - ziemlich alt) des Oracle-Treibers verschiedene Fehler enthielt, die einen Deadlock verursachten (wie im oben genannten TP-Processor2-Status zu sehen). Es wurde erst aktiv, als wir auf die neue Umgebung umgestiegen sind. Durch das Aktualisieren auf die neueste Version (ojdbc14) wurde das Problem auf dem Primärserver behoben.

Jordy Boom
quelle
Dies führte mich zu meiner richtigen Lösung: Ich hatte eine Sperre in einer DB-Zeile ... und bekam keine Ausnahme im App-Server
cljk
6

Aus der Beschreibung geht hervor, dass das Problem möglicherweise darauf zurückzuführen ist, dass die Datenbankabfragen zu lange dauern. Wenn die Abfragen länger dauern, dauert die Anforderung länger und daher werden mehr Abfragen gleichzeitig ausgeführt. Wie Sie sehen, gehen Ihnen die Kater-Threads aus. Wenn Sie das Problem mit der Datenbank lösen, sollten Sie in Ordnung sein.

  • Holen Sie sich einen Stack-Trace, entweder mit jstack oder mit kill -3 $ process_id. Sehen Sie, was Ihre Threads tun, wenn es stirbt. Wenn alle auf die Datenbank warten, ist das ein guter Hinweis auf meine Theorie. Sie könnten alle auf ein Schloss warten.
  • Installieren Sie LambdaProbe. Es ist von unschätzbarem Wert, um herauszufinden, was Ihr Kater tut.
  • Rüsten Sie Ihren Kater auf. 5.5.8 ist unglaublich alt. Ich denke, sie sind jetzt am 5.5.27.
David Pashley
quelle
David, ich habe die Frage (siehe Update 1) mit neuen Erkenntnissen aktualisiert, die auf Ihrem Vorschlag für einen Thread-Dump / Stack-Trace basieren.
Jordy Boom
Ich würde vorschlagen, dass Ihr Datenbankverbindungspool im Vergleich zu Ihrem maximalen Tomcat-Verbindungswert zu klein ist. Es scheint, dass die meisten Threads auf eine Datenbankverbindung warten.
David Pashley
Der einzige Grund, warum es so viele Threads gibt, ist, dass die normalerweise verwendeten Threads auf diesen einen Thread warten, der versucht, aus dem Socket zu lesen. Die Anzahl der zu einem bestimmten Zeitpunkt verwendeten DB-Verbindungen liegt zwischen 1 und 3. Niemals werden mehr als so viele benötigt.
Jordy Boom
5

Fügen Sie connectionTimeout und keepAliveTimeout zu Ihrem AJP-Connector hinzu, der sich in /etc/tomcat7/server.xml befindet.

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

Informationen zum AJP-Connector finden Sie unter https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html

  • connectionTimeout = Die Anzahl der Millisekunden, die dieser Connector nach dem Akzeptieren einer Verbindung auf die Anzeige der Anforderungs-URI-Zeile wartet. Der Standardwert für AJP-Protokollconnectors ist -1 (dh unendlich).

  • keepAliveTimeout = Die Anzahl der Millisekunden, die dieser Connector auf eine weitere AJP-Anforderung wartet, bevor die Verbindung geschlossen wird. Der Standardwert ist die Verwendung des Werts, der für das connectionTimeout-Attribut festgelegt wurde.

Wenn die Werte connectionTimeout und keepAliveTimeout nicht definiert sind, werden AJP-Verbindungen unendlich lange am Leben erhalten. Aufgrund vieler Threads beträgt die Standard-Maximalanzahl 200.

Ich empfehle die Installation von psi-probe - einem erweiterten Manager und Monitor für Apache Tomcat, der von Lambda Probe bereitgestellt wird. https://code.google.com/p/psi-probe/

paalfe
quelle
4

Aufgrund der Funktionsweise von AJP können die dauerhaften Verbindungen zwischen Apache (entweder mit mod_proxy_ajp oder mod_jk) nur vom Client sicher geschlossen werden . In diesem Fall ist der Client der Apache-Worker, der geöffnet wird, und hält dann eine Verbindung zu Tomcat für das Leben für den Worker-Prozess .

Aufgrund dieses Verhaltens können Sie nicht mehr Apache-Worker als Tomcat-Worker-Threads haben. Andernfalls können zusätzliche http-Worker keine Verbindung zu Tomcat herstellen (da die Annahmewarteschlange voll ist), und Ihr Backend wird als DOWN markiert!

Dave Cheney
quelle
1
Entschuldigen Sie den Kommentar nach all den Jahren, aber kann dies nicht garantiert werden, indem Sie das max-Flag in der ProxyPass-Konfiguration auf die Anzahl der MaxThreads des Servlet-Containers setzen?
Horst Gutmann
2

Ich habe mit mod_proxy bessere Ergebnisse erzielt als mit mod_ajp in Bezug auf die Stabilität. Probieren Sie diese Lösung aus. Es ist nicht invasiv - bestenfalls löst es das Problem und schlimmstenfalls schließt es mod_ajp aus.

Abgesehen davon hört es sich so an, als würden Ihre Tomcats nicht mehr reagieren und alle Anfragethreads sind blockiert. Lassen Sie Ihr Entwicklerteam untersuchen, was gerade passiert. Es ist hilfreich , einen Thread-Dump zu erstellen und an sie zu senden.

Robert Munteanu
quelle
Ich hatte den Eindruck, dass mod_proxy einige Skalierbarkeitsprobleme aufweist, obwohl es einfacher zu verbinden ist. Es scheint, dass die Apache-Stiftung mod_jk ( wiki.apache.org/tomcat/FAQ/Connectors#Q2 )
Ophidian
Es bietet keine klebrige Sitzung, stimmt. Aber sonst hatte ich nie Probleme damit.
Robert Munteanu
1

Das erste, woran ich denke, wenn ich höre, dass ein Server eine Weile läuft, plötzlich langsamer wird und dann Dienstausfälle auftreten, ist, dass ihm der Arbeitsspeicher ausgeht und der Swap ausgeht. Ich bin mir nicht sicher, ob die AJP-Fehler, die Sie sehen, auf Zeitüberschreitungen zurückzuführen sein könnten, aber es scheint nicht völlig unvernünftig zu sein. Ich sehe jedoch keine offensichtliche Möglichkeit, eine Verbindung zur Netzwerkkarte herzustellen. In jedem Fall empfehle ich Ihnen, sich ein Bild davon zu machen, was mit Ihrer Speichernutzung passiert, wenn diese Ereignisse eintreten.

Wenn Ihnen der Arbeitsspeicher ausgeht, müssen Sie möglicherweise Ihren Apache herunterfahren MaxClientsund Ihren erhöhen ListenBacklog.

Übrigens, vielen Dank, dass Sie Ihre Frage so gut organisiert und vollständig gestellt haben.

Chaos
quelle
Wenn ich währenddessen "top" beobachte, bleibt die Speichernutzung ziemlich konsistent. Zumindest gibt es keine Spikes. Es gibt nur einen kurzen Moment der hohen CPU-Auslastung.
Jordy Boom
1

Ich hatte ähnliche Protokollfehler in der Redhat-Umgebung mit proxy_ajp und Tomcat. Behebung durch Aktualisierung des httpd-Pakets:

yum update httpd

von:

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

zu:

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

Starten Sie dann Apache neu, gefolgt von Tomcat.

Das hat es für mich behoben!

Bass
quelle