sviluppo-web-qa.it

Apache + Tomcat ha problemi di comunicazione. Messaggi di errore non chiari. Realizzazione di siti Web ospitati su Tomcat

Setup:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache inoltra le richieste utilizzando AJP.

Problema:
Dopo un certo periodo di tempo (nessuna costante, può essere tra un'ora o due o uno o più giorni) Tomcat scenderà. O smette di rispondere o inserisce il generico "Servizio temporaneamente non disponibile".

Diagnosi:
Esistono due server con la stessa configurazione. Uno ospita un sito Web a traffico più elevato (diverse richieste al secondo), l'altro uno a basso traffico (una manciata di richieste ogni pochi minuti). Entrambi i siti Web sono basi di codice completamente diverse, ma presentano problemi simili.

Sul primo server, quando si verifica il problema, tutti i thread iniziano lentamente a essere ripresi fino a raggiungere il limite (MaxThreads 200). A quel punto il server non risponde più (e viene visualizzata la pagina di servizio non disponibile dopo un lungo periodo di tempo).

Sul secondo server, quando si verifica il problema, le richieste impiegano molto tempo e quando vengono eseguite, viene visualizzata solo la pagina del servizio non disponibile.

Oltre alla menzione del problema MaxThreads, i registri Tomcat non indicano alcun problema specifico che potrebbe causare questo.

Tuttavia, nei registri di Apache vediamo messaggi casuali che si riferiscono ad AJP. Ecco un esempio di messaggio casuale che vediamo (in nessun ordine specifico):

[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'altra cosa strana che abbiamo notato sul server a traffico più elevato è che proprio prima che si verifichi il problema, le query del database impiegano molto più tempo rispetto a prima (2000-5000 ms contro normalmente 5-50ms). Questo dura solo 2-4 secondi prima che venga visualizzato il messaggio MaxThreads. Suppongo che questo sia il risultato del server che improvvisamente ha a che fare con troppi dati/traffico/thread.

Informazioni di base:
Questi due server erano in esecuzione senza problemi da un po 'di tempo. I sistemi sono stati effettivamente installati ciascuno utilizzando due schede di rete durante quel periodo. Hanno separato il traffico interno ed esterno. Dopo un aggiornamento della rete, abbiamo spostato questi server in singole NIC (questo ci è stato consigliato per motivi di sicurezza/semplicità). Dopo tale modifica, i server hanno iniziato ad avere questi problemi.

Risoluzione:
La soluzione ovvia sarebbe quella di tornare a una configurazione di due schede di rete. I problemi sono che ciò comporterebbe alcune complicazioni con la configurazione della rete e sembra ignorare il problema. Preferiremmo provare a farlo funzionare su un singolo NIC.

Cercare su Google i vari messaggi di errore non ha fornito nulla di utile (soluzioni vecchie o non correlate al nostro problema).

Abbiamo provato a regolare i vari timeout, ma ciò ha reso il server leggermente più lungo prima di morire.

Non siamo sicuri di dove cercare per diagnosticare ulteriormente il problema. Stiamo ancora cercando di capire quale potrebbe essere il problema:

1) L'impostazione con AJP e Tomcat è errata o obsoleta (ad esempio bug noti?)
2) L'impostazione della rete (due NIC contro una NIC) sta causando problemi di confusione o velocità effettiva.
3) I siti Web stessi (non esiste un codice comune, nessuna piattaforma utilizzata, solo basic Java con servlet e JSP)

Aggiornamento 1:
Seguendo l'utile consiglio di David Pashley, ho fatto un dump dello stack/thread durante il problema. Quello che ho scoperto è che tutti i 200 thread erano in uno dei seguenti stati:

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

Curiosamente, solo un thread su tutti i 200 thread era in questo stato:

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

È possibile che il driver Oracle in questo thread stia forzando tutti gli altri thread ad attendere il completamento. Per qualche motivo deve essere bloccato in questo stato di lettura (il server non ripristina mai da solo, richiede un riavvio).

Ciò suggerisce che deve essere correlato alla rete tra il server e il database o al database stesso. Stiamo continuando gli sforzi di diagnosi, ma qualsiasi suggerimento sarebbe utile.

22
Jordy Boom

Si scopre che questa versione (classi 12 - piuttosto vecchia) del driver Oracle presentava vari bug che causavano un deadlock (come visto nello stato TP-Processor2 citato sopra). Non è diventato attivo fino a quando non siamo passati al nuovo ambiente. L'aggiornamento all'ultima versione (ojdbc14) ha risolto il problema sul server primario.

9
Jordy Boom

Dalla descrizione, suggerirei che il problema potrebbe essere dovuto alle query del database che impiegano troppo tempo. Se le query impiegano più tempo, la richiesta richiederà più tempo e quindi ne avrai molte in esecuzione contemporaneamente. Come vedi, stai esaurendo i thread Tomcat. Quando risolvi il problema con il database dovresti essere a posto.

  • Ottieni una traccia dello stack, usando jstack o usando kill -3 $ process_id. Guarda cosa stanno facendo i tuoi fili quando muore. Se stanno tutti aspettando sul database, questo è un buon indicatore della mia teoria. Potrebbero essere tutti in attesa di qualche blocco.
  • Installa LambdaProbe. È prezioso per scoprire cosa sta facendo il tuo Tomcat.
  • Aggiorna il tuo Tomcat. 5.5.8 è incredibilmente vecchio. Penso che siano ora in 5.5.27.
6
David Pashley

Aggiungi connectionTimeout e keepAliveTimeout al tuo connettore AJP che si trova in /etc/Tomcat7/server.xml.

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

Informazioni sul connettore AJP su https://Tomcat.Apache.org/Tomcat-7.0-doc/config/ajp.html

  • connectionTimeout = Il numero di millisecondi che questo connettore attenderà, dopo aver accettato una connessione, per la presentazione della riga URI della richiesta. Il valore predefinito per i connettori del protocollo AJP è -1 (ovvero infinito).

  • keepAliveTimeout = Il numero di millisecondi che questo connettore attenderà per un'altra richiesta AJP prima di chiudere la connessione. Il valore predefinito è utilizzare il valore che è stato impostato per l'attributo connectionTimeout.

Se i valori connectionTimeout e keepAliveTimeout non sono definiti, le connessioni AJP saranno mantenute in vita per infinito. Causando molti thread, il numero massimo di thread predefinito è 200.

Consiglio di installare psi-probe, un gestore e monitor avanzato per Apache Tomcat, creato da Lambda Probe. https://code.google.com/p/psi-probe/

5
paalfe

A causa del modo in cui funziona AJP, le connessioni persistenti tra Apache (usando mod_proxy_ajp o mod_jk) possono essere chiuse in modo sicuro dal client. In questo caso, il client è il lavoratore Apache che si apre e quindi mantiene una connessione a Tomcat per durata del processo di lavoro.

A causa di questo comportamento non è possibile avere più lavoratori Apache rispetto ai thread dei lavoratori Tomcat. In questo modo, i lavoratori http aggiuntivi non potranno connettersi a Tomcat (poiché la coda di accettazione è piena) e contrassegnare il back-end come DOWN!

4
Dave Cheney

Ho ottenuto risultati migliori con mod_proxy anziché mod_ajp in termini di stabilità, quindi prova questa soluzione. Non è invasivo: nella migliore delle ipotesi risolverà il problema e nella peggiore delle ipotesi escluderà mod_ajp.

A parte questo, sembra che i tuoi Tomcats smettano di rispondere e che tutti i thread delle richieste siano collegati. Chiedi al tuo team di sviluppo di scoprire cosa sta succedendo - prendere un dump del thread e consegnarlo a loro sarà utile.

2
Robert Munteanu

La prima cosa a cui penso quando sento che un server funziona per un po ', improvvisamente rallenta e poi inizia ad avere errori di servizio è che si sta esaurendo RAM e thrashing swap. chiarire se gli errori AJP che si stanno verificando potrebbero essere conseguenti a timeout, ma non sembra del tutto irragionevole; non vedere alcun modo ovvio che si collegherebbe alla scheda di rete, tuttavia. In ogni caso, ti consiglio di ottenere un immagine di ciò che sta accadendo con l'utilizzo della memoria quando si verificano questi eventi.

Se si sta esaurendo la RAM, potrebbe essere necessario abbassare Apache MaxClients e aumentare ListenBacklog.

A proposito, grazie per aver reso la tua domanda così ben organizzata e completa.

1
chaos

Ho avuto errori di registro simili nell'ambiente Redhat con proxy_ajp e Tomcat. Risolto aggiornando il pacchetto httpd:

yum update httpd

a partire dal:

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

per:

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

Quindi riavviato Apache, seguito dal riavvio di Tomcat.

Questo mi ha risolto!

1
Bass