设置:
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-5000 毫秒,而通常为 5-50 毫秒)。在 MaxThreads 消息出现之前,这只持续 2-4 秒。我假设这是服务器突然处理太多数据/流量/线程的结果。
背景信息:
这两台服务器已经运行了很长一段时间没有问题。在此期间,这些系统实际上是使用两个 NIC 设置的。他们将内部和外部流量分开。网络升级后,我们将这些服务器移至单个 NIC(出于安全/简单的原因,我们建议这样做)。更改后,服务器开始出现这些问题。
解决方案:
显而易见的解决方案是回到两个 NIC 的设置。这样做的问题是它会导致网络设置的一些复杂性,并且似乎忽略了这个问题。我们更愿意尝试让它在单个 NIC 设置上运行。
谷歌搜索各种错误消息并没有提供任何有用的信息(旧的解决方案或与我们的问题无关)。
我们已经尝试调整各种超时,但这只会让服务器在死机前运行稍长一些。
我们不确定在哪里可以进一步诊断问题。我们仍然在抓住可能出现的问题的稻草:
1) AJP 和 Tomcat 的设置不正确或过时(即已知错误?)
2) 网络设置(两个 NIC 与一个 NIC)导致混乱或吞吐量问题。
3) 网站本身(没有通用代码,没有使用平台,只有带有 servlet 和 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 驱动程序正在强制所有其他线程等待它完成。由于某种原因,它必须停留在这种读取状态(服务器永远不会自行恢复,它需要重新启动)。
这表明它必须与服务器和数据库之间的网络或数据库本身有关。我们正在继续进行诊断工作,但任何提示都会有所帮助。
事实证明,Oracle 驱动程序的这个版本(classes12 - 相当旧)中有各种导致死锁的错误(如上面引用的 TP-Processor2 状态所示)。直到我们切换到新环境,它才变得活跃。升级到最新版本 (ojdbc14) 已解决主服务器上的问题。
从描述中,我认为问题可能是由于数据库查询花费的时间太长。如果查询花费的时间更长,请求将花费更长的时间,因此您将同时运行更多的查询。如您所见,您的tomcat 线程已用完。当您解决数据库问题时,您应该没问题。
将 connectionTimeout 和 keepAliveTimeout 添加到 /etc/tomcat7/server.xml 中的 AJP 连接器。
有关 AJP 连接器的信息,位于https://tomcat.apache.org/tomcat-7.0-doc/config/ajp.html
connectionTimeout = 在接受连接后,此连接器将等待呈现请求 URI 行的毫秒数。AJP 协议连接器的默认值为 -1(即无限)。
keepAliveTimeout = 此连接器在关闭连接之前等待另一个 AJP 请求的毫秒数。默认值是使用已为 connectionTimeout 属性设置的值。
如果未定义 connectionTimeout 和 keepAliveTimeout 值,则 AJP 连接将无限期保持活动状态。由于线程多,默认最大线程数为 200。
我建议安装 psi-probe - Apache Tomcat 的高级管理器和监视器,从 Lambda Probe 派生。https://code.google.com/p/psi-probe/
由于 AJP 的工作方式,apache 之间的持久连接(使用 mod_proxy_ajp 或 mod_jk)只能由客户端安全地关闭。在这种情况下,客户端是打开的 apache 工作程序,然后在工作进程的生命周期内保持与 tomcat 的连接。
由于这种行为,您不能拥有比 tomcat 工作线程更多的 apache 工作线程。这样做会导致额外的 http worker 无法连接到 tomcat(因为接受队列已满),并将您的后端标记为 DOWN!
就稳定性而言,我使用 mod_proxy 而不是 mod_ajp 获得了更好的结果,因此请尝试该解决方案。它是非侵入性的——充其量它会解决问题,最坏的情况是它会排除 mod_ajp。
除此之外,听起来您的 Tomcat 停止响应并且所有请求线程都被占用了。让您的开发团队调查正在发生的事情 -进行线程转储并将其交付给他们将很有用。
当我听到服务器运行了一段时间后突然变慢然后开始出现服务故障时,我首先想到的是它的 RAM 用完并颠簸交换。我不清楚您看到的 AJP 失败是否可能是由于超时造成的,但这似乎并非完全不合理;不过,看不到任何明显的方式可以连接到 NIC。无论如何,我建议您了解这些事件发生时您的内存使用情况。
如果您的 RAM 用完了,您可能需要关闭 Apache
MaxClients
并增加ListenBacklog
.顺便说一句,感谢您使您的问题如此有条理和完整。
我在使用 proxy_ajp 和 Tomcat 的 Redhat 环境中遇到了类似的日志错误。通过更新httpd包解决:
从:
至:
然后重新启动apache,然后重新启动Tomcat。
那为我修好了!