在高并发系统中,服务的稳定性至关重要。当后端服务出现故障或性能瓶颈时,如果没有有效的保护机制,可能导致请求积压、资源耗尽,最终引发整个系统的级联故障,也就是我们常说的“服务雪崩”。熔断机制(Circuit Breaking)正是应对此类问题的关键手段之一。
Nginx 作为高性能的 Web 服务器和反向代理服务器,在高并发环境下常用于流量控制,以保障系统稳定性。它常被用于实现限流与熔断机制,以保护后端服务并平滑流量峰值。

例如,在金融风控场景中,当 Nginx 检测到后端风控服务的错误率持续上升时,可以通过内嵌的 Lua 脚本将该服务标记为熔断状态。此后,来自客户端的相关请求将被直接拦截,并返回一个预定义的降级结果(如默认风控结论),同时触发异步告警通知运维人员。系统会周期性地探测后端服务的恢复情况,待服务稳定后,再逐步恢复流量,避免一次性洪峰导致服务再次被压垮。
Nginx如何实现熔断?
熔断的核心思想是:当后端服务的错误率升高或响应延迟显著增加时,主动阻断或降级对该服务的请求,以避免故障扩散。虽然 Nginx 本身不内置完整的熔断器状态机(如开路、半开路、闭路),但我们可以借助其强大的可扩展性来实现。
一种常见的方案是使用 OpenResty(集成了 LuaJIT 的 Nginx)编写 Lua 脚本。通过 Lua 脚本,我们可以在共享内存中统计请求的成功与失败情况,基于预设的阈值(如错误率、超时比例)来判断是否触发熔断,并控制熔断的持续时间与恢复策略。

以下是一个基于 OpenResty Lua 脚本实现的简易熔断示例。该脚本会在一个时间窗口内统计特定接口的请求失败情况,当失败率达到阈值时,触发熔断。
lua_shared_dict cb_metrics 10m;
server {
listen 80;
server_name api.example.com;
location /api/report/ {
access_by_lua_block {
local dict = ngx.shared.cb_metrics
local state = dict:get(“report_state”) or “closed”
local open_until = dict:get(“report_open_until”) or 0
local now = ngx.now()
-- 如果处于熔断打开状态且未到恢复时间,则直接返回熔断响应
if state == “open” and now < open_until then
ngx.status = 503
ngx.say(‘{“code”:503,”msg”:”report service temporarily unavailable”}’)
return ngx.exit(ngx.HTTP_OK)
end
}
proxy_pass http://upstream_report;
log_by_lua_block {
local dict = ngx.shared.cb_metrics
local key_err = “report_err”
local key_total = “report_total”
-- 累计统计窗口内的请求总数
dict:incr(key_total, 1, 0)
local status = ngx.status
local cost = ngx.now() - ngx.req.start_time()
-- 判断是否为失败请求(HTTP状态码>=500 或 响应耗时>2秒)
if status >= 500 or cost > 2 then
dict:incr(key_err, 1, 0)
end
local total = dict:get(key_total)
local err = dict:get(key_err)
-- 当窗口内请求数达到50次时,进行错误率评估
if total and total >= 50 then
local err_rate = err / total
-- 如果错误率超过50%,则触发熔断,打开状态持续30秒
if err_rate > 0.5 then
dict:set(“report_state”, “open”)
dict:set(“report_open_until”, ngx.now()+30)
else
dict:set(“report_state”, “closed”)
end
-- 重置统计窗口,开始下一个周期的计数
dict:set(key_total, 0)
dict:set(key_err, 0)
end
}
}
}
代码逻辑解读:
access_by_lua_block:在请求转发到后端之前执行。它检查共享字典中 report_service 的熔断状态。如果状态为“open”(打开)且当前时间未超过熔断恢复时间,则直接向客户端返回 503 状态码和提示信息,请求不会到达后端。
log_by_lua_block:在请求结束后执行。它负责收集请求的指标:
- 累加总请求数 (
key_total)。
- 如果响应状态码为5xx或请求耗时超过2秒,则累加错误数 (
key_err)。
- 熔断决策:每累计50个请求作为一个统计窗口。当窗口满时,计算错误率。若错误率超过50%,则将熔断状态设置为“open”,并设置30秒的恢复时间。否则,保持“closed”(关闭)状态。决策后重置计数器,开始新的统计窗口。
通过以上机制,当 /api/report/ 接口在短时间内错误率过高时,Nginx 会主动将其熔断30秒。在此期间,所有对该接口的请求都会在Nginx层面被快速失败,返回预设的降级响应,从而有效保护后端服务不被持续冲击,为服务的恢复赢得时间。
这种在网关/代理层实现的熔断,是构建高可用架构的有效实践之一。如果你想深入探讨更多关于系统架构、高并发处理的话题,欢迎在云栈社区与其他开发者一起交流。
|