Apache + Tomcat ma problemy z komunikacją. Niejasne komunikaty o błędach. Wyłączanie witryn hostowanych pod Tomcat

22

Konfiguracja:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache przekazuje żądania za pomocą AJP.

Problem:
Po pewnym czasie (brak stałej, może wynosić od godziny do dwóch lub jednego lub więcej dni) Tomcat spadnie. Albo przestaje odpowiadać, albo wyświetla ogólny „Usługa tymczasowo niedostępna”.

Diagnoza:
istnieją dwa serwery z tą samą konfiguracją. Jedna z nich zawiera witrynę o wyższym natężeniu ruchu (kilka żądań na sekundę), a druga o niskim ruchu (garść żądań co kilka minut). Obie strony są zupełnie różnymi bazami kodów, ale wykazują podobne problemy.

Na pierwszym serwerze, gdy pojawia się problem, wszystkie wątki powoli zaczynają być podejmowane aż do osiągnięcia limitu (MaxThreads 200). W tym momencie serwer przestał odpowiadać (i po dłuższym czasie pojawia się strona niedostępna dla usługi).

Na drugim serwerze, gdy wystąpi problem, żądania zajmują dużo czasu, a po ich zakończeniu widać tylko stronę niedostępną dla usługi.

Poza wzmianką o problemie MaxThreads, dzienniki Tomcat nie wskazują żadnych konkretnych problemów, które mogą to powodować.

Jednak w dziennikach Apache widzimy losowe komunikaty dotyczące AJP. Oto próbka losowego komunikatu, który widzimy (bez określonej kolejności):

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

Inną dziwną rzeczą, którą zauważyliśmy na serwerze o wyższym natężeniu ruchu, jest to, że tuż przed wystąpieniem problemu zapytania bazy danych trwają znacznie dłużej niż wcześniej (2000-5000 ms w porównaniu z normalnie 5-50ms). Trwa to tylko 2-4 sekundy, zanim pojawi się komunikat MaxThreads. Zakładam, że jest to wynikiem nagłego radzenia sobie przez serwer ze zbyt dużą ilością danych / ruchu / wątków.

Informacje podstawowe:
Te dwa serwery działały bez problemu od dłuższego czasu. Systemy były konfigurowane za pomocą dwóch kart sieciowych w tym czasie. Rozdzielili ruch wewnętrzny i zewnętrzny. Po aktualizacji sieci przenieśliśmy te serwery do pojedynczych kart sieciowych (było to nam zalecane ze względów bezpieczeństwa / prostoty). Po tej zmianie serwery zaczęły mieć te problemy.

Rozwiązanie:
Oczywistym rozwiązaniem byłoby powrót do konfiguracji dwóch kart sieciowych. Problem polega na tym, że spowodowałoby to pewne komplikacje w konfiguracji sieci i wydaje się, że zignorowanie problemu. Wolelibyśmy spróbować uruchomić go na jednej konfiguracji karty sieciowej.

Przeglądanie różnych komunikatów o błędach nie zapewnia niczego przydatnego (ani starych rozwiązań, ani nie ma związku z naszym problemem).

Próbowaliśmy dostosować różne limity czasu, ale właśnie to spowodowało, że serwer działał nieco dłużej przed śmiercią.

Nie jesteśmy pewni, gdzie szukać dalszej diagnozy problemu. Nadal chwytamy się słomy, na czym polega problem:

1) Konfiguracja z AJP i Tomcat jest niepoprawna lub nieaktualna (tzn. Znane błędy?)
2) Konfiguracja sieci (dwie karty sieciowe i jedna karta sieciowa) powoduje problemy z pomyłką lub przepustowością.
3) Same strony internetowe (nie ma wspólnego kodu, nie używa się platform, tylko podstawowy kod Java z serwletami i JSP)

Aktualizacja 1:
Postępując zgodnie z pomocnymi wskazówkami Davida Pashleya, zrobiłem stos śledzenia / zrzutu wątku podczas problemu. Odkryłem, że wszystkie 200 wątków było w jednym z następujących stanów:

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

Co ciekawe, tylko jeden wątek ze wszystkich 200 wątków był w tym stanie:

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

Możliwe, że sterownik Oracle w tym wątku zmusza wszystkie pozostałe wątki do oczekiwania na zakończenie. Z jakiegoś powodu musi utknąć w tym stanie odczytu (serwer nigdy nie odzyskuje samodzielnie, wymaga ponownego uruchomienia).

Sugeruje to, że musi to być związane z siecią między serwerem a bazą danych lub samą bazą danych. Kontynuujemy wysiłki diagnostyczne, ale wszelkie wskazówki byłyby pomocne.

Jordy Boom
źródło
Po pierwsze, jest to wyjątkowo napisane pytanie. Fantastyczna robota w szczegółach! Po drugie, czy używasz proxy_ajp lub mod_jk do łączenia serwerów Apache i Tomcat?
Ophidian
Używam proxy_ajp do połączenia tych dwóch.
Jordy Boom
Wykonuj testy warunków skrajnych za pomocą oblężenia, joedog.org/siege-home .
paalfe

Odpowiedzi:

9

Okazuje się, że ta wersja (klasy 12 - dość stara) sterownika Oracle zawierała różne błędy, które powodowały impas (jak widać w cytowanym powyżej stanie TP-Processor2). Nie stało się aktywne, dopóki nie zmieniliśmy się na nowe środowisko. Aktualizacja do najnowszej wersji (ojdbc14) rozwiązała problem na serwerze podstawowym.

Jordy Boom
źródło
To doprowadziło mnie do mojego właściwego rozwiązania: miałem blokadę w wierszu DB ... i nigdy nie dostałem żadnego wyjątku w App-Server
cljk
6

Z opisu sugeruję, że problem może wynikać z zbyt długich zapytań do bazy danych. Jeśli zapytania trwają dłużej, żądanie potrwa dłużej, dlatego będziesz mieć ich więcej na raz. Jak widzisz, zabrakło Ci wątków tomcat. Po rozwiązaniu problemu z bazą danych powinno być w porządku.

  • Uzyskaj ślad stosu, albo używając jstack, albo kill -3 $ process_id. Zobacz, co robią twoje wątki, gdy umiera. Jeśli wszyscy czekają w bazie danych, to dobry wskaźnik do mojej teorii. Wszyscy mogą czekać na jakiś zamek.
  • Zainstaluj LambdaProbe. Jest nieoceniony, gdy dowiesz się, co robi twój kocur.
  • Uaktualnij swojego kocura. 5.5.8 jest niesamowicie stary. Myślę, że są teraz w wersji 5.5.27.
David Pashley
źródło
David, zaktualizowałem pytanie (patrz Aktualizacja 1) o nowe ustalenia na podstawie sugestii śledzenia zrzutu / stosu wątków.
Jordy Boom
Sugeruję, że pula połączeń z bazą danych jest zbyt mała w porównaniu z maksymalną wartością połączenia tomcat. Wygląda na to, że większość wątków czeka na połączenie z bazą danych.
David Pashley,
Jedynym powodem jest to, że wiele wątków polega na tym, że zwykle używane wątki czekają na ten jeden wątek, który próbuje odczytać z gniazda. Liczba połączeń DB wykorzystywanych w dowolnym momencie wynosi od 1 do 3. Nigdy nie trzeba więcej niż tylu.
Jordy Boom,
5

Dodaj connectionTimeout i keepAliveTimeout do łącznika AJP w pliku /etc/tomcat7/server.xml.

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

Informacje o złączu AJP na https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html

  • connectionTimeout = Liczba milisekund, które ten łącznik będzie czekał, po zaakceptowaniu połączenia, na wyświetlenie wiersza URI żądania. Domyślna wartość dla złącz protokołu AJP to -1 (tj. Nieskończona).

  • keepAliveTimeout = Liczba milisekund, które ten łącznik będzie czekał na kolejne żądanie AJP przed zamknięciem połączenia. Domyślną wartością jest użycie wartości ustawionej dla atrybutu connectionTimeout.

Jeśli wartości connectionTimeout i keepAliveTimeout nie zostaną zdefiniowane, połączenia AJP będą utrzymywane przy życiu przez nieskończoność. Powodując wiele wątków, domyślna maksymalna liczba wątków wynosi 200.

Polecam instalację psi-sondy - zaawansowanego menedżera i monitora dla Apache Tomcat, opracowanego z Lambda Probe. https://code.google.com/p/psi-probe/

Paalfe
źródło
4

Ze względu na sposób działania AJP trwałe połączenia między Apache (przy użyciu mod_proxy_ajp lub mod_jk) mogą być bezpiecznie zamknięte tylko przez klienta . W takim przypadku klient jest robotem Apache, który się otwiera, a następnie utrzymuje połączenie z tomcat na całe życie procesu roboczego .

Z powodu tego zachowania nie można mieć większej liczby pracowników Apache niż wątków roboczych Tomcat. Spowoduje to, że dodatkowi pracownicy HTTP nie będą mogli połączyć się z tomcat (ponieważ kolejka akceptująca jest pełna) i oznaczy twój backend jako DOWN!

Dave Cheney
źródło
1
Przepraszam za komentarz po tylu latach, ale czy nie można tego zagwarantować, ustawiając flagę max w konfiguracji ProxyPass na liczbę MaxThreads kontenera serwletu?
Horst Gutmann
2

Mam lepsze wyniki z mod_proxy zamiast mod_ajp pod względem stabilności, więc wypróbuj to rozwiązanie. Jest nieinwazyjny - w najlepszym wypadku rozwiąże problem, aw najgorszym przypadku wykluczy mod_ajp.

Poza tym wydaje się, że Twoje Tomcats przestają odpowiadać, a wszystkie wątki żądań są powiązane. Poproś zespół deweloperów, aby sprawdził, co się dzieje - przydatne będzie zrobienie zrzutu wątku i dostarczenie go do nich.

Robert Munteanu
źródło
Miałem wrażenie, że mod_proxy ma pewne problemy ze skalowalnością, mimo że łatwiej go podłączyć. Wydaje się, że fundacja Apache zaleca mod_jk ( wiki.apache.org/tomcat/FAQ/Connectors#Q2 )
Ophidian
To prawda, że ​​nie zapewnia lepkiej sesji. Ale poza tym nigdy nie miałem z tym problemów.
Robert Munteanu,
1

Pierwszą rzeczą, o której myślę, kiedy słyszę, że serwer działa przez chwilę, nagle spowalnia, a następnie zaczyna mieć awarie usług, jest to, że kończy mu się pamięć RAM i miażdżąca wymiana. Nie jestem pewien, czy awarie AJP, które widzisz, mogą być konsekwencją przekroczenia limitu czasu, ale nie wydaje się to całkowicie nieracjonalne; nie widzę jednak żadnego oczywistego sposobu połączenia z kartą sieciową. W każdym razie zalecam, aby uzyskać obraz tego, co dzieje się z użyciem pamięci, gdy te zdarzenia się pojawią.

Jeśli kończy Ci się pamięć RAM, może być konieczne wyłączenie Apache MaxClients i zwiększenie ListenBacklog.

Nawiasem mówiąc, dziękuję za uczynienie twojego pytania tak dobrze zorganizowanym i kompletnym.

chaos
źródło
Kiedy obserwuję „top”, gdy to się dzieje, użycie pamięci pozostaje dość spójne. Przynajmniej nie ma kolców. Jest tylko krótki moment dużego obciążenia procesora.
Jordy Boom
1

Miałem podobne błędy dziennika w środowisku Redhat z proxy_ajp i Tomcat. Rozwiązane przez aktualizację pakietu httpd:

yum update httpd

z:

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

do:

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

Następnie zrestartowałem apache, a następnie ponownie uruchomiłem Tomcat.

To naprawiło to dla mnie!

Gitara basowa
źródło