
설정:
Fedora 8
Apache 2.2.8
Tomcat 5.5.8
Apache는 AJP를 사용하여 요청을 전달합니다.
문제:
특정 기간(전혀 상수가 아니며 한 시간, 두 시간 또는 하루 이상이 될 수 있음)이 지나면 Tomcat이 작동 중지됩니다. 응답을 중지하거나 일반 '서비스를 일시적으로 사용할 수 없음'을 표시합니다.
진단:
동일한 설정을 가진 두 개의 서버가 있습니다. 하나는 트래픽이 높은 웹 사이트(초당 여러 요청)를 수용하고, 다른 하나는 트래픽이 낮은 웹 사이트(몇 분마다 몇 개의 요청)를 수용합니다. 두 웹사이트는 완전히 다른 코드베이스이지만 유사한 문제를 나타냅니다.
첫 번째 서버에서 문제가 발생하면 모든 스레드가 제한(MaxThreads 200)에 도달할 때까지 천천히 사용되기 시작합니다. 그 시점에서 서버는 더 이상 응답하지 않습니다(오랜 시간이 지나면 서비스를 사용할 수 없다는 페이지가 나타납니다).
두 번째 서버에서 문제가 발생하면 요청에 오랜 시간이 걸리고 요청이 완료되면 서비스를 사용할 수 없다는 페이지만 표시됩니다.
MaxThreads 문제에 대한 언급 외에 Tomcat 로그에는 이 문제를 일으킬 수 있는 특정 문제가 표시되지 않습니다.
그러나 Apache 로그에는 AJP를 참조하는 임의의 메시지가 표시됩니다. 다음은 표시되는 무작위 메시지의 샘플입니다(특정 순서 없음).
[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)
트래픽이 높은 서버에서 발견한 또 다른 이상한 점은 문제가 발생하기 직전에 데이터베이스 쿼리가 이전보다 훨씬 오래 걸린다는 것입니다(2000-5000ms 대 일반적으로 5-50ms). 이는 MaxThreads 메시지가 나타나기 전까지 2~4초 동안만 지속됩니다. 나는 이것이 서버가 갑자기 너무 많은 데이터/트래픽/스레드를 처리한 결과라고 가정합니다.
배경 정보:
이 두 서버는 꽤 오랫동안 문제 없이 운영되어 왔습니다. 실제로 시스템은 그 기간 동안 두 개의 NIC를 사용하여 각각 설정되었습니다. 내부 트래픽과 외부 트래픽을 분리했습니다. 네트워크 업그레이드 후 우리는 이러한 서버를 단일 NIC로 옮겼습니다(보안/단순성 이유로 권장됨). 변경 후 서버에 이러한 문제가 발생하기 시작했습니다.
해결:
확실한 해결책은 두 개의 NIC 설정으로 다시 돌아가는 것입니다. 그 문제는 네트워크 설정에 약간의 복잡성을 야기할 수 있다는 것이며 문제를 무시하는 것처럼 보입니다. 우리는 단일 NIC 설정에서 실행하는 것을 선호합니다.
다양한 오류 메시지를 인터넷 검색해도 유용한 정보가 제공되지 않았습니다(오래된 솔루션이거나 문제와 관련이 없음).
우리는 다양한 시간 제한을 조정해 보았지만 이로 인해 서버가 죽기 전에 약간 더 오래 실행되었습니다.
문제를 더 자세히 진단하려면 어디를 봐야 할지 잘 모르겠습니다. 우리는 문제가 무엇인지 아직도 파악하지 못하고 있습니다.
1) AJP 및 Tomcat 설정이 올바르지 않거나 오래되었습니다(예: 알려진 버그?).
2) 네트워크 설정(NIC 2개 대 NIC 1개)으로 인해 혼란이나 처리량 문제가 발생합니다.
3) 웹사이트 자체(공통 코드도 없고, 사용되는 플랫폼도 없으며, 서블릿과 JSP가 포함된 기본 Java 코드만 있음)
업데이트 1:
David Pashley의 유용한 조언에 따라 문제가 발생하는 동안 스택 추적/스레드 덤프를 수행했습니다. 내가 발견한 것은 200개의 스레드가 모두 다음 상태 중 하나에 있다는 것입니다.
"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]
흥미롭게도 전체 200개의 스레드 중 단 하나의 스레드만이 이 상태에 있었습니다.
"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]
이 스레드의 Oracle 드라이버가 다른 모든 스레드가 완료될 때까지 기다리도록 강제할 수 있습니다. 어떤 이유로 이 읽기 상태에서 멈춰야 합니다(서버는 자체적으로 복구되지 않으며 다시 시작해야 함).
이는 서버와 데이터베이스 사이의 네트워크 또는 데이터베이스 자체와 관련되어야 함을 의미합니다. 우리는 진단 노력을 계속하고 있지만 어떤 조언이라도 도움이 될 것입니다.
답변1
Oracle 드라이버의 이 버전(classes12 - 꽤 오래된)에는 교착 상태를 일으키는 다양한 버그가 있는 것으로 나타났습니다(위에 인용된 TP-Processor2 상태에서 볼 수 있듯이). 새로운 환경으로 전환할 때까지 활성화되지 않았습니다. 최신 버전(ojdbc14)으로 업그레이드하면 기본 서버의 문제가 해결되었습니다.
답변2
설명을 보면 데이터베이스 쿼리에 시간이 너무 오래 걸리기 때문에 문제가 발생할 수 있다고 생각됩니다. 쿼리가 더 오래 걸리면 요청도 더 오래 걸리므로 한 번에 더 많은 쿼리를 실행하게 됩니다. 보시다시피 Tomcat 스레드가 부족합니다. 데이터베이스 문제를 해결하면 괜찮을 것입니다.
- jstack을 사용하거나 kill -3 $process_id를 사용하여 스택 추적을 가져옵니다. 스레드가 종료되면 스레드가 무엇을 하는지 확인하세요. 그들이 모두 데이터베이스를 기다리고 있다면 그것은 내 이론에 대한 좋은 지적입니다. 그들은 모두 잠금을 기다리고 있을 수도 있습니다.
- LambdaProbe를 설치합니다. Tomcat이 무엇을 하고 있는지 알아내는 데 매우 중요합니다.
- 톰캣을 업그레이드하세요. 5.5.8은 엄청나게 오래되었습니다. 지금은 5.5.27에 있는 것 같아요.
답변3
/etc/tomcat7/server.xml에 있는 AJP 커넥터에 ConnectionTimeout 및 keepAliveTimeout을 추가합니다.
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"
connectionTimeout="10000" keepAliveTimeout="10000" />
AJP 커넥터에 대한 정보는 다음을 참조하세요.https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html
ConnectionTimeout = 이 커넥터가 연결을 수락한 후 요청 URI 줄이 표시될 때까지 기다리는 시간(밀리초)입니다. AJP 프로토콜 커넥터의 기본값은 -1(즉, 무한)입니다.
keepAliveTimeout = 이 커넥터가 연결을 닫기 전에 다른 AJP 요청을 기다리는 시간(밀리초)입니다. 기본값은 ConnectionTimeout 속성에 설정된 값을 사용하는 것입니다.
ConnectionTimeout 및 keepAliveTimeout 값이 정의되지 않은 경우 AJP 연결은 무한정 유지됩니다. 스레드가 많아지므로 기본 최대 스레드는 200개입니다.
Lambda Probe에서 분기된 Apache Tomcat용 고급 관리자이자 모니터인 psi-probe를 설치하는 것이 좋습니다.https://code.google.com/p/psi-probe/
답변4
안정성 측면에서 mod_ajp 대신 mod_proxy를 사용하면 더 나은 결과를 얻었으므로 해당 솔루션을 사용해 보세요. 비침습적입니다. 기껏해야 문제를 해결할 수 있고 최악의 경우 mod_ajp를 배제할 수 있습니다.
그 외에는 Tomcat이 응답을 멈추고 모든 요청 스레드가 묶인 것처럼 들립니다. 개발팀에게 무슨 일이 일어나고 있는지 조사하도록 하세요.스레드 덤프 가져오기그리고 그것을 그들에게 전달하는 것이 유용할 것입니다.