nginx 不支持集群节点间心跳同步,仅能主动探测后端服务健康状态;需通过主动检查模块、语义化健康接口、状态暴露与真实流量反馈四层机制实现可靠感知。

Nginx 本身不自带“集群节点间心跳同步”能力,它无法让多个 Nginx 实例互相感知、共享状态或自动协调。所谓“后端心跳感知”,实际是指 Nginx 主动探测其 upstream 中的业务后端(如 Java 应用、Node.js 服务等)是否存活且可用,而非 Nginx 节点之间的心跳。要实现可靠的状态感知,需分层配置:主动探测 + 语义判断 + 状态暴露 + 动态响应。
一、启用主动健康检查,避免被动等待失败
Nginx 原生 max_fails/fail_timeout 是被动机制——得先发请求、失败5次才下线,会浪费流量和延迟。推荐使用 nginx_upstream_check_module(Tengine 内置,或为 Nginx 手动编译集成)做主动探测:
-
在
upstream块中添加check指令,例如:upstream backend_api { server 10.0.1.10:8080; server 10.0.1.11:8080; check interval=3 rise=2 fall=3 timeout=1 type=http; check_http_send "GET /health HTTP/1.1\r\nHost: api.example.com\r\nConnection: close\r\n\r\n"; check_http_expect_alive http_2xx; default_down on; # 启动时默认标记为 down,等探测通过再上线 } -
关键参数说明:
-
interval=3:每3秒探测一次 -
rise=2:连续2次成功才标记为 up,防抖动 -
fall=3:连续3次失败才标记为 down,防误判 -
timeout=1:单次探测超时设为1秒,避免阻塞 -
check_http_expect_alive http_2xx:只认 2xx 响应为健康,明确拒绝 503/404 等“假活”状态
-
二、让探测结果反映真实业务能力
仅看 HTTP 状态码远远不够。后端 /health 接口应返回结构化 JSON,并检查关键字段:
- 示例响应:
{ "status": "UP", "db": "UP", "diskFree": 1258291200 } - 若使用 OpenResty,可用
lua-resty-http+cjson在log_by_lua_block中解析响应体,提取status或db字段; - 避免探测路径被 WAF 拦截——将
/health加入白名单,或改用type=tcp作为兜底(只验端口连通性); - 不要用
HEAD请求替代GET,某些框架的健康端点对方法敏感,HEAD可能绕过实际检查逻辑。
三、暴露可读、可采、可联动的状态视图
探测结果必须能被运维查看、监控系统采集、告警平台触发:
- 添加状态页 location:
location /status { check_status json; # 输出 JSON 格式,含 IP、状态、失败次数、最后响应时间等 allow 192.168.10.0/24; # 限制内网访问 deny all; } - 对接 Prometheus:用
nginx-module-vts或自定义 Lua exporter 抓取upstream指标,如upstream_response_time、upstream_status分布; - 设置告警规则,例如:“单节点 5 分钟内 5xx 错误率 >5%” 或 “节点连续 3 次探测返回 DOWN”;
- 日志中记录
$upstream_addr $upstream_response_time $upstream_status,便于事后定位拐点。
四、结合真实流量反馈,补全被动观测维度
主动探测再勤快,也覆盖不了所有接口路径和并发压力场景。真实请求是最终验证:
- 开启容错重试:
proxy_next_upstream error timeout http_502 http_503 http_504; proxy_next_upstream_tries 3;
- 在
log_format中打标节点级表现,例如:log_format detailed '$remote_addr - $upstream_addr [$time_local] ' '"$request" $status $body_bytes_sent ' '$upstream_response_time $upstream_status'; - 若用 OpenResty,可在
log_by_lua_block中统计各节点的 P95 延迟、5xx 比率,写入shared_dict,供balancer_by_lua_block动态降权或跳过。
不复杂但容易忽略。











