如果你已经在用 Apache 2.4+ 且只需要基础的负载均衡和反向代理,mod_proxy_ajp 是更轻量的选择;但如果需要实时监控、热重载 worker 或关键业务高可用,mod_jk 仍是更稳妥的方案。
先说结论:mod_proxy_ajp 配置简单、维护成本低,适合中小规模集群;mod_jk 功能完整、有监控页面,适合对稳定性要求高的生产环境。
- 适合:mod_proxy_ajp 适合已启用 mod_proxy 系列模块、追求轻量维护的场景
- 重点看:mod_jk 支持 jkstatus 实时监控和 worker 热重载,mod_proxy_ajp 不支持
- 别忽略:ProxyPass 路径结尾斜杠必须严格一致,stickysession 大小写敏感,Tomcat 端需配置 jvmRoute 及 AJP secret 安全密钥
核心差异与选型
Apache 和 Tomcat 整合本质是让 Apache 处理静态内容和反向代理,Tomcat 专注执行 Java Servlet/JSP。两者通讯主要有两种主流方式:mod_jk 和 mod_proxy_ajp。
AJP(Apache JServ Protocol)是定向包协议,采用二进制形式代替文本形式,Web 服务器通过 TCP 连接 Servlet 容器,多个请求可以循环重用同一个连接。mod_proxy_ajp 是 Apache 2.4+ 原生模块,无需额外编译或下载 mod_jk.so,维护成本低。但 mod_proxy_ajp 不支持运行时热重载 worker 列表,也不提供类似 jkstatus 的实时监控页面。
mod_jk 需要单独管理 workers.properties、jkstatus 页面,版本兼容性更敏感,但强在稳定性和性能方面,有关键业务监控需求时更可靠。
安全加固与版本要求
版本建议:请使用 Apache 2.4+ 版本,Apache 2.2 已停止维护且存在已知安全风险。
安全高危预警:Tomcat AJP 协议曾存在 CVE-2020-1938 (Ghostcat) 漏洞。在生产环境配置 AJP Connector 时,必须设置 secret 属性,并建议将 AJP 端口限制在内网 IP 或 localhost,严禁暴露在公网。
分步配置实操
第一步:确认 Apache 版本和已加载模块
执行命令检查当前模块状态:
httpd -v
httpd -M | grep proxy
如果看到 proxy_module、proxy_ajp_module、proxy_balancer_module 已加载,说明可以直接用 mod_proxy_ajp 方案。
第二步:Apache 端配置 mod_proxy_ajp
在 httpd.conf 或虚拟主机配置中加载模块(Apache 2.4+ 通常默认加载,按需取消注释):
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
配置负载均衡集群(注意动静分离,仅代理动态请求路径):
<Proxy balancer://cluster>
BalancerMember ajp://192.168.123.110:8009 route=tomcat1
BalancerMember ajp://192.168.123.120:8009 route=tomcat2
ProxySet stickysession=JSESSIONID|jsessionid nofailover=On
</Proxy>
# 仅代理动态路径,静态资源由 Apache 直接处理
ProxyPass /app balancer://cluster/app
ProxyPassReverse /app balancer://cluster/app
注意 ProxyPass 和 ProxyPassReverse 的路径结尾斜杠必须严格一致,否则后端会收到错误路径。避免使用 ProxyPass / 全量代理,以免 bypass Apache 静态资源处理能力。
第三步:Tomcat 端 AJP Connector 配置
在 server.xml 中配置 AJP Connector,必须添加 secret 属性:
<Connector port='8009' protocol='AJP/1.3'
secret='YourStrongSecretKey'
maxThreads='200'
connectionTimeout='60000'
keepAliveTimeout='60000'
redirectPort='8443' />
在<Engine>标签里加 jvmRoute,这是 sticky session 的路由依据:
<Engine name='Catalina' defaultHost='localhost' jvmRoute='tomcat1'>
第四步:如果选择 mod_jk 方案
需要下载对应 Apache 版本的 mod_jk.so,放入 modules 文件夹,配置 workers.properties 文件和 mod_jk.conf,并启用 jkstatus 监控页面。
怎么验证是否生效
检查 Apache 配置语法:
httpd -t
如果返回 Syntax OK,配置语法正确。
查看已加载模块:
httpd -M | grep -E 'proxy|jk'
访问应用后检查 Tomcat 日志,确认请求是否通过 AJP 端口到达。如果用 mod_jk,可以访问 jkstatus 页面查看 worker 状态和连接情况。
测试 sticky session 是否生效:多次刷新页面,观察是否始终路由到同一台 Tomcat(可通过在每台 Tomcat 首页显示不同标识来验证)。
安全验证:使用 netstat 确认 AJP 端口(默认 8009)仅监听在内网 IP 或 127.0.0.1,不应暴露在公网接口。
常见坑
503 错误排查方向:看到 Service Temporarily Unavailable 第一反应不是改 httpd.conf,而是检查 Tomcat 端 AJP Connector 的 maxThreads 是否与 Apache 的 MaxRequestWorkers 匹配、connectionTimeout 是否设置(AJP 默认是 -1 永不超时,容易导致连接卡死)、jvmRoute 是否漏配。
ProxyPass 路径斜杠问题:常见错误是把 ProxyPass /app ajp://127.0.0.1:8009/app 和 ProxyPass / balancer://cluster/混用在同一虚拟主机里,结果请求被重复代理或路径错乱。集群场景下只用 balancer://协议,且结尾斜杠必须一致。
stickysession 大小写敏感:JSESSIONID 大小写敏感,Tomcat 默认生成的是 JSESSIONID,不是 jsessionid。Windows 下不区分,Linux 下可能失效。
nofailover 参数含义:nofailover=On 表示 session 绑定后不漂移,适合有状态应用;若设为 Off,故障时会尝试转发到其他节点,但 session 数据可能丢失。
mod_proxy_ajp 的监控盲区:不支持运行时热重载 worker 列表,也不提供实时监控页,这点常被忽略,直到集群出问题时才发现无法快速定位哪台 Tomcat 掉线了。
CVE-2020-1938 漏洞风险:未配置 secret 的 AJP 连接器可能被利用读取 Web 应用配置文件或上传文件。务必在 Tomcat 9.0.31+ 及相应版本中配置 secret 属性。
参考来源
- Apache HTTP Server 官方文档:mod_proxy_ajp Module
- Apache Tomcat 官方文档:AJP Connector Configuration
- Apache Tomcat 安全公告:CVE-2020-1938