1

我们有一对 Apache 2.4 Web 服务器 ( web02, web03) 正在mod_proxy_ajp与一对 Tomcat 7.0.59 服务器 ( app02, app03) 通信。

Tomcat 服务器是备用服务器,除非完全脱机app03,否则不应获取流量。app02

web02 和 web03 上的 Apache 配置:

<Proxy balancer://ajp_cluster>
  BalancerMember ajp://app02:8009 route=worker1 ping=3 retry=60
  BalancerMember ajp://app03:8009 status=+R route=worker2 ping=3 retry=60
  ProxySet stickysession=JSESSIONID|jsessionid lbmethod=byrequests
</Proxy>

app02 和 app03 上 AJP 的 Tomcat 配置:

<Connector protocol="AJP/1.3" URIEncoding="UTF-8" port="8009" />

我们看到 Apache 开始向其发送流量的问题,app03即使app02仍然可用但可能有点忙,这些流量仍被标记为备用。

Apache SSL 错误日志:

[Thu Sep 12 14:23:28.028162 2019] [proxy_ajp:error] [pid 24234:tid 140543375898368] (70007)The timeout specified has expired: [client 207.xx.xxx.7:1077] AH00897: cping/cpong failed to 10.160.160.47:8009 (app02)
[Thu Sep 12 14:23:28.028196 2019] [proxy_ajp:error] [pid 24234:tid 140543375898368] [client 207.xx.xxx.7:1077] AH00896: failed to make connection to backend: app02
[Thu Sep 12 14:23:28.098869 2019] [proxy_ajp:error] [pid 24135:tid 140543501776640] [client 207.xx.xxx.7:57809] AH01012: ajp_handle_cping_cpong: ajp_ilink_receive failed, referer: https://site.example.com/cart
[Thu Sep 12 14:23:28.098885 2019] [proxy_ajp:error] [pid 24135:tid 140543501776640] (70007)The timeout specified has expired: [client 207.xx.xxx.7:57809] AH00897: cping/cpong failed to 10.160.160.47:8009 (app02), referer: https://site.example.com/cart

我们的 Apache 日志中有数百条这样的消息。

app02除非完全脱机,否则有关使 Apache 坚持使用的设置的任何建议?

4

1 回答 1

4

您在 Tomcat 连接器中遇到线程耗尽,导致 httpd 认为 app02 处于错误状态 - 在某种程度上,确实如此。

简短的回答是切换您的 Tomcat AJP 连接器以使用protocol="org.apache.coyote.ajp.AjpNioProtocol"

长答案是,嗯,更长。

mod_jk 使用 httpd 和 Tomcat 之间的持久连接。对此的历史论据是性能。它节省了为每个请求建立新的 TCP 连接的时间。通常,测试表明该参数不成立,并且建立新的 TCP 连接或执行 CPING/CPONG 以确认连接有效(如果您使用持久连接,则需要这样做)所花费的时间足够接近同一时间。不管怎样,持久连接是 mod_jk 的默认设置。

当使用持久连接时,mod_jk 会为每个 httpd 工作线程创建一个连接,并将该连接缓存在工作线程中。

Tomcat 7.x 中的默认 AJP 连接是 BIO 连接器。此连接器使用阻塞 I/O,并且每个连接需要一个线程。

当 httpd 配置的工作线程多于 Tomcat 的线程数时,就会出现此问题。最初一切正常。当 httpd worker 遇到需要传递给 Tomcat 的第一个请求时,mod_jk 会为该 httpd worker 创建持久连接并提供请求。该 httpd 工作人员处理的需要传递给 Tomcat 的后续请求将使用该缓存连接。请求被(有效地)随机分配给 httpd 工作人员。随着越来越多的 httpd 工作人员看到需要传递给 Tomcat 的第一个请求,mod_jk 为每个工作人员创建必要的持久连接。与 Tomcat 的许多连接很可能大部分都是空闲的。空闲程度取决于 httpd 上的负载以及传递给 Tomcat 的那些请求的比例。

一切都很好,直到更多的 httpd 工作人员需要创建到 Tomcat 的连接,并且 Tomcat 具有线程。请记住,Tomcat AJP BIO 连接器每个连接都需要一个线程,因此 maxThreads 本质上是 Tomcat 允许的最大 AJP 连接数。此时 mod_jk 无法创建请求,因此启动了故障转移过程。

有两种解决方案。第一个 - 我上面描述的那个 - 是删除每个连接限制一个线程。通过切换到 NIO AJP 连接器,Tomcat 使用 Poller 线程来维护 1000 条连接,只将那些有数据处理的连接传递给一个线程进行处理。Tomcat 处理的限制是 maxThreads 是 Tomcat 可以在该连接器上处理的最大并发请求数。

第二种解决方案是禁用持久连接。mod_jk 创建一个连接,将其用于单个请求,然后关闭它。这减少了 mod_jk 在 httpd 和 Tomcat 之间任意一点所需的连接数。

对不起,上面是相当大的文字墙。我还在包括这个在内的各种演示文稿中对此进行了介绍

于 2019-09-23T09:20:16.560 回答