Nginx 对后端 Tomcat 的健康检查分为被动检查(开源版默认)和主动检查(商业版或通过第三方模块实现)两种。
一、被动健康检查(开源 Nginx 原生支持)
原理:Nginx 在转发请求时,如果发现某个后端节点出现问题,会将其标记为不可用,并在fail_timeout时间内不再转发请求给它。
配置示例
upstream tomcat_cluster { server 192.168.1.10:8080 max_fails=3 fail_timeout=30s; server 192.168.1.11:8080 max_fails=3 fail_timeout=30s; server 192.168.1.12:8080 max_fails=3 fail_timeout=30s; } server { listen 80; location / { proxy_pass http://tomcat_cluster; proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; } }参数说明
| 参数 | 说明 |
|---|---|
max_fails=3 | 允许的最大失败次数 |
fail_timeout=30s | 失败超时时间。30秒内如果失败3次,则标记为不可用,后续30秒内不再转发请求 |
proxy_next_upstream | 定义什么情况下认为"失败",会尝试下一个节点 |
proxy_next_upstream可配置的状态:
| 状态 | 说明 |
|---|---|
error | 连接后端时发生网络错误 |
timeout | 连接或读取后端超时 |
invalid_header | 后端返回无效的响应头 |
http_500 | 后端返回500错误 |
http_502 | 后端返回502错误 |
http_503 | 后端返回503错误 |
http_504 | 后端返回504错误 |
off | 关闭重试机制 |
被动检查的局限性
- 必须有用户请求才会触发检查。如果节点已经坏了但没人访问,Nginx 不会主动发现
- 故障节点恢复后,不会自动回归。需要等待
fail_timeout时间后,再接收少量请求"试探",成功后才逐步恢复流量 - 不能提前摘除即将出问题的节点(如CPU飙高、Full GC频繁)
二、主动健康检查(Nginx Plus 或 nginx-upsync 模块)
原理:Nginx 主动向后端节点发送探测请求(如 HTTP GET /health),根据响应判断节点健康状态,自动摘除或恢复。
Nginx Plus 配置示例(商业版)
upstream tomcat_cluster { zone upstream_zone 64k; server 192.168.1.10:8080; server 192.168.1.11:8080; health_check interval=5s fails=3 passes=2 uri=/health match=health_ok; } match health_ok { status 200; header Content-Type = application/json; body ~ "status.*UP"; }参数说明
| 参数 | 说明 |
|---|---|
interval=5s | 每5秒检查一次 |
fails=3 | 连续3次失败,标记为不可用 |
passes=2 | 连续2次成功,标记为可用 |
uri=/health | 检查后端哪个接口 |
match | 匹配规则(状态码、响应头、响应体) |
开源替代方案:nginx-upsync + consul
社区常用的方案是使用nginx-upsync模块结合consul实现动态主动健康检查。
三、Tomcat 端需要提供的健康检查接口
无论用哪种方式,Tomcat 都需要提供一个健康的检查接口(例如/health),返回当前节点的健康状态。
方案1:Spring Boot Actuator(标准做法)
# application.ymlmanagement:endpoints:web:exposure:include:health,readyendpoint:health:show-details:alwayshealth:db:enabled:trueredis:enabled:true访问/actuator/health返回:
{"status":"UP","components":{"db":{"status":"UP"},"diskSpace":{"status":"UP"},"redis":{"status":"UP"}}}方案2:自定义专用健康检查接口(更精细)
@RestControllerpublicclassHealthController{@AutowiredprivateDataSourcedataSource;@AutowiredprivateRedisTemplateredisTemplate;@GetMapping("/health")publicResponseEntity<Map<String,Object>>health(){Map<String,Object>status=newHashMap<>();// 检查数据库连接try{dataSource.getConnection().close();status.put("database","UP");}catch(Exceptione){status.put("database","DOWN: "+e.getMessage());}// 检查Redis连接try{redisTemplate.getConnectionFactory().getConnection().ping();status.put("redis","UP");}catch(Exceptione){status.put("redis","DOWN");}// 判断整体状态booleanisHealthy=status.values().stream().allMatch(v->v.toString().startsWith("UP"));returnResponseEntity.status(isHealthy?HttpStatus.OK:HttpStatus.SERVICE_UNAVAILABLE).body(status);}}四、生产环境最佳实践:主被动结合
| 检查方式 | 作用 | 实现位置 |
|---|---|---|
| 被动检查 | 兜底,防止主动检查漏掉的故障 | Nginxmax_fails+proxy_next_upstream |
| 主动检查 | 提前发现故障,快速摘除 | Nginx Plus 或nginx-upsync |
| 应用层健康接口 | 提供真实的健康状态(不仅进程存活,还要检查依赖) | Tomcat/health接口 |
| 监控告警 | 节点频繁抖动时报警,人工介入 | Zabbix/Prometheus |
配置建议:
upstream tomcat_cluster { # 被动检查:兜底 server 192.168.1.10:8080 max_fails=3 fail_timeout=30s; server 192.168.1.11:8080 max_fails=3 fail_timeout=30s; # 主动检查:Nginx Plus 或 upsync 模块 # health_check interval=5s fails=3 passes=2 uri=/health; keepalive 32; # 保持连接池 } location / { proxy_pass http://tomcat_cluster; proxy_next_upstream error timeout http_500 http_502 http_503; proxy_next_upstream_tries 2; # 最多重试2次 proxy_connect_timeout 3s; # 连接超时3秒 proxy_read_timeout 30s; # 读取超时30秒 }五、常见问题排查
| 现象 | 可能原因 | 解决 |
|---|---|---|
| 后端已宕机,Nginx 还在转发 | 被动检查未触发(无请求进来) | 增加主动健康检查 |
| 健康检查总是失败 | /health接口没实现或返回非200 | curl -v http://tomcat:8080/health手动测试 |
| 节点一直在"抖动"(频繁被摘除和恢复) | 健康检查超时时间太短,或后端压力大响应慢 | 调整fail_timeout、interval,优化后端 |
| 重启 Tomcat 后流量不恢复 | fail_timeout未过,或被动检查还没尝试 | 配置主动健康检查,或缩短fail_timeout |
六、一句话总结
Nginx 开源版默认只支持被动健康检查(靠转发失败来发现故障),生产环境建议配合主动健康检查(Nginx Plus 或
nginx-upsync模块),同时让 Tomcat 提供/health接口,检查数据库、Redis 等依赖的真实状态,实现精准的节点摘除和恢复。