FEATURED · 精选文章

Nginx主动健康检查

发布时间 / 2026/8/16 3:42:22
来源 / 创域科博编辑部
栏目 / 资讯中心
Nginx主动健康检查 一、引言被动健康检查的“致命盲区”在绝大多数Nginx配置中upstream的健康保障依赖于max_fails和fail_timeout这两个参数。这是一种被动健康检查Passive Health Check机制只有当真实用户请求打到某个后端节点并失败时Nginx才会将其标记为不可用。这种“用真实流量试错”的模式在生产环境中存在三个致命缺陷首请求必损节点刚恢复或刚上线时第一批请求必然命中尚未被标记的故障节点用户体验直接受损故障感知滞后若某节点流量占比低如权重1/10可能需要数十秒甚至数分钟才能积累足够的失败次数触发摘除恢复探测粗暴fail_timeout到期后Nginx直接将节点重新放入池中没有渐进式验证若节点未完全恢复新一轮真实请求再次成为“炮灰”。主动健康检查Active Health Check正是为解决这些问题而生。它由Nginx独立发起周期性探测请求与业务流量完全隔离实现✅ 故障提前发现用户请求零损伤✅ 新节点上线前预检通过后才接入流量✅ 恢复过程可控支持慢启动和渐进放量✅ 多维度判定不仅看TCP连通性还验证HTTP状态码、响应体内容、响应时间等。本文将从开源与商业版的方案对比出发深度拆解主动健康检查的配置语义、高级策略和生产级落地模板帮你构建真正“用户无感”的后端容错体系。二、方案选型三条技术路线的全景对比本文后续内容聚焦OpenResty方案因其是开源生态中最接近Nginx Plus能力的生产级选择且原理可迁移至其他方案。选型建议K8s环境优先使用Ingress Controller的原生健康检查与Pod Readiness Probe联动非K8s 预算充足Nginx Plus是最优解功能完整、官方支持非K8s 开源需求OpenResty lua-resty-upstream-healthcheck是事实标准极简场景/学习原生被动检查足够但务必理解其局限。三、OpenResty主动健康检查核心架构3.1 工作原理┌─────────────────────────────────────────────────────┐ │ OpenResty Worker │ │ │ │ ┌──────────────┐ ┌───────────────────────────┐ │ │ │ Timer Module │───▶│ Health Check Coroutine │ │ │ │ (定时触发) │ │ 1. 遍历upstream节点列表 │ │ │ └──────────────┘ │ 2. 发起HTTP/TCP探测请求 │ │ │ │ 3. 校验响应(状态码/Body) │ │ │ ┌──────────────┐ │ 4. 更新共享内存中的健康状态 │ │ │ │ Shared Dict │◀──▶│ │ │ │ │ (健康状态存储)│ └───────────────────────────┘ │ │ └──────┬───────┘ │ │ │ 读取 │ │ ┌──────▼───────┐ │ │ │ Balancer │ ← 业务请求到达时仅选择健康节点 │ │ │ (负载均衡器) │ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────┘关键设计健康检查运行在独立协程中不阻塞业务请求处理健康状态存储在shared dict中跨worker共享避免重复探测Balancer阶段只读取状态、不做探测保证请求处理延迟不受影响。3.2 核心组件安装# 确保OpenResty已安装 # 安装lua-resty-upstream-healthcheck luarocks install lua-resty-upstream-healthcheck # 或使用opm推荐 opm get openresty/lua-resty-upstream-healthcheck四、基础配置从零搭建主动健康检查4.1 最小可用配置http { # 共享内存存储健康状态 lua_shared_dict healthcheck 10m; # 初始化健康检查器 init_worker_by_lua_block { local hc require resty.upstream.healthcheck local ok, err hc.spawn_checker({ shm healthcheck, upstream api_backend, type http, http_req GET /health HTTP/1.1\r\nHost: api-backend\r\n\r\n, interval 2000, -- 每2秒探测一次 timeout 1000, -- 探测超时1秒 fall 3, -- 连续3次失败 → 标记不健康 rise 2, -- 连续2次成功 → 标记健康 valid_statuses {200}, -- 仅200视为健康 }) if not ok then ngx.log(ngx.ERR, failed to spawn health checker: , err) end } # Upstream定义 upstream api_backend { server 10.0.1.10:8080; server 10.0.1.11:8080; server 10.0.1.12:8080; } server { location /api/ { proxy_pass http://api_backend; } # 健康检查状态查看接口 location /upstream_health { content_by_lua_block { local hc require resty.upstream.healthcheck local status hc.get_status(api_backend) ngx.say(status) } } } }4.2 核心参数详解参数类型默认值说明生产建议shmstring必填shared dict名称与lua_shared_dict一致upstreamstring必填upstream块名称必须精确匹配typestringhttp探测协议http/tcpAPI用httpDB/TCP服务用tcphttp_reqstring必填原始HTTP请求报文包含完整Header以\r\n\r\n结尾intervalnumber1000探测间隔(ms)2000~5000过短增加后端负担timeoutnumber1000单次探测超时(ms)≤interval/2避免探测堆积fallnumber3连续失败阈值2~5过小误判过大延迟risenumber2连续成功阈值2~3防止抖动节点反复上下线valid_statusestable{200}健康状态码列表按需添加204/301等concurrencynumber1并发探测数节点多时调大避免串行延迟⚠️关键注意http_req必须是完整的原始HTTP请求包括方法、路径、协议版本、Host头和空行。缺少任何部分都会导致探测失败。推荐使用string.format动态构造http_req string.format( GET %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: nginx-healthcheck\r\nConnection: close\r\n\r\n, /health, api-backend )五、高级策略超越“通/不通”的精细化治理5.1 多维度健康判定-- 自定义校验函数状态码 响应体 响应时间三重验证 local function custom_checker(resp_status, resp_body, resp_time) -- 条件1状态码必须200 if resp_status ~ 200 then return false end -- 条件2响应体必须包含OK if not resp_body or not string.find(resp_body, status%s*:%s*ok) then return false end -- 条件3响应时间不超过500ms if resp_time 500 then return false end return true end hc.spawn_checker({ -- ... 其他参数 checker custom_checker, -- 替代valid_statuses })价值后端返回200但实际处于降级状态如数据库连接池耗尽、缓存全miss时传统状态码检查无法识别。内容延迟双重校验能捕获这类“假健康”节点。5.2 差异化探测策略不同后端服务的健康特征不同应为每个upstream定制探测参数服务类型intervaltimeoutfallrise校验重点核心API2s1s32状态码响应体延迟内部微服务3s2s22状态码即可数据库代理5s3s33TCP连通SELECT 1第三方API10s5s53状态码宽松静态资源源站5s2s22HEAD 2005.3 与新节点上线联动-- 新节点加入upstream后先执行预检再放行流量 local function pre_check_new_node(host, port) local hc require resty.upstream.healthcheck local ok hc.single_check(api_backend, host, port, { timeout 2000, valid_statuses {200}, }) if ok then ngx.log(ngx.INFO, new node , host, :, port, passed pre-check) -- 调用服务发现API注册节点 else ngx.log(ngx.WARN, new node , host, :, port, failed pre-check, skipping) end end零停机发布的关键新Pod/容器启动后先通过主动健康检查验证就绪再注册到upstream。彻底消除“刚上线就被打挂”的经典问题。5.4 慢启动与渐进放量OpenResty原生不支持slow_start可通过自定义Balancer实现local node_recovery_time {} -- shared dict记录节点恢复时间 function balanced_peer(premature, upstream_name) local peers get_healthy_peers(upstream_name) local now ngx.now() for _, peer in ipairs(peers) do local recovery_ts node_recovery_time[peer.id] if recovery_ts then local elapsed now - recovery_ts if elapsed 30 then -- 30秒慢启动窗口 -- 按时间比例降低权重 peer.weight math.floor(peer.base_weight * (elapsed / 30)) else node_recovery_time[peer.id] nil -- 恢复正常 end end end return select_peer_by_weight(peers) end价值节点恢复后立即承受全量流量可能导致二次崩溃如JIT未预热、连接池为空、缓存冷启动。慢启动让流量线性增长给后端充分的“热身”时间。六、可观测性健康检查本身的监控6.1 暴露健康状态APIlocation /nginx_upstream_status { content_by_lua_block { local cjson require cjson.safe local hc require resty.upstream.healthcheck local result {} local upstreams {api_backend, auth_backend, cache_backend} for _, name in ipairs(upstreams) do result[name] hc.get_status(name) end ngx.header.content_type application/json ngx.say(cjson.encode(result)) } }6.2 Prometheus指标导出-- 在/content_metrics中输出 local hc require resty.upstream.healthcheck local status hc.get_status(api_backend) -- 解析status字符串提取各节点状态 for node, state in pairs(parse_status(status)) do ngx.say(string.format( nginx_upstream_health{upstreamapi_backend,node%s} %d, node, state healthy and 1 or 0 )) end6.3 必采监控指标指标含义告警阈值健康节点数当前可用后端数量 总数×50% P1节点频繁翻转1小时内健康状态变化次数5次 P2探测成功率成功探测 / 总探测90% P2平均探测延迟探测请求P99耗时timeout×80% P2全部节点不健康持续时长30s P0新节点预检失败率上线前检查失败占比10% P2七、生产安全检查清单检查项状态说明shared dict大小充足☐按节点数×256B估算预留2倍余量探测路径专用且轻量☐/health不应查库/调外部服务timeout interval/2☐防止探测任务堆积fall ≥ 2, rise ≥ 2☐避免网络抖动导致误判探测请求含Connection: close☐避免占用后端长连接新节点上线前有预检☐杜绝“上线即故障”健康状态API已暴露☐供监控和运维排查使用探测日志独立记录☐不与业务日志混合多upstream差异化配置☐核心服务更敏感边缘服务更宽松定期演练故障切换☐验证健康检查实际生效八、常见踩坑速查表现象根因解决方案健康检查始终失败http_req格式错误补全HTTP/1.1、Host头、空行节点健康但请求仍502Balancer未读取shared dict确认balancer_by_lua中使用hc API探测超时频发timeout过短或后端/health过重增大timeout或简化健康接口节点频繁上下线fall/rise1调整为fall3, rise2shared dict报错内存不足增大lua_shared_dict容量新节点上线即被打挂无预检或无慢启动添加pre-check 渐进放量探测占用大量后端连接未加Connection: close修改http_req添加该Header多worker重复探测未使用shared dict确认shm参数正确健康状态API返回空upstream名称不匹配检查spawn_checker中的upstream参数Reload后健康状态丢失shared dict未持久化正常行为reload后自动重建九、结语感谢您的阅读如果你有任何疑问或想要分享的经验请在评论区留言交流
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻