【问题标题】:Apache mod_proxy_ajp module prematurely sending traffic to spare backend serverApache mod_proxy_ajp 模块过早地将流量发送到备用后端服务器
【发布时间】:2020-01-21 19:47:03
【问题描述】:

我们有一对运行 mod_proxy_ajp 的 Apache 2.4 Web 服务器(web02web03)与一对 Tomcat 7.0.59 服务器(app02app03)通信。

app03 上的 Tomcat 服务器是备用服务器,除非 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 日志中有数百条此类消息。

关于使 Apache 坚持使用app02 的设置有什么建议,除非它完全离线?

【问题讨论】:

  • “完全离线”的标准是什么?
  • @DusanBajic 我们只想在 app02 Tomcat 处于错误状态并停止提供页面或出现故障时将流量转移到 app03
  • 但这不是你现在所拥有的:“无法连接到后端:app02”,所以 app02 Tomcat 处于错误状态并停止提供页面,因此流量翻转到 app03 (我认为这不是理想的行为,但是您必须更准确地了解“不良状态”和“停止服务页面”的含义)
  • 另外,一般来说,虽然在 apache 中微调负载平衡可能是可行的,但也许您可能想考虑切换到更现代的反向代理,如 haproxy 或 nginx?

标签: apache tomcat mod-proxy ajp mod-proxy-ajp


【解决方案1】:

您在 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 之间任意一点所需的连接数。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-02
    • 2019-03-22
    • 1970-01-01
    • 1970-01-01
    • 2022-01-12
    • 2010-12-17
    • 1970-01-01
    相关资源
    最近更新 更多