OpenResty可通过resty.http实现可控的带指数退避的Lua层请求重试,支持连接/读写超时设置、错误捕获及状态码判断,无需依赖Nginx原生upstream重试机制。

OpenResty 通过 Lua 脚本可灵活实现请求重试与故障自动转移,核心在于利用 resty.http 客户端的连接/读写超时控制、错误捕获机制,配合 upstream 的健康检查或手动状态维护,实现服务降级与流量切换。
使用 resty.http 实现带退避的请求重试
在 Lua 中调用远程服务时,不依赖 Nginx 原生 upstream 重试(如 proxy_next_upstream),而是由 Lua 主动控制重试逻辑,更可控、可定制。
- 引入
resty.http模块,创建复用的 http 对象(推荐用 cosocket 连接池) - 设置合理的超时:connect_timeout(建连)、send_timeout(发包)、read_timeout(收包),避免长阻塞
- 对常见失败类型(如 connection refused、timeout、502/503/504)进行捕获并重试
- 加入指数退避(exponential backoff),例如第 1 次延时 0.1s,第 2 次 0.2s,第 3 次 0.4s
示例片段:
local httpc = require "resty.http".new()httpc:set_timeouts(1000, 1000, 2000) -- conn/send/read ms
for i = 1, 3 do
local res, err = httpc:request_uri("https://api.example.com/v1/data", {
method = "GET",
headers = { ["X-Request-ID"] = ngx.req.get_headers()["X-Request-ID"] }
})
if res and res.status >= 200 and res.status return ngx.say(res.body)
end
if i ngx.sleep(0.1 * (2 ^ (i - 1))) -- 退避延迟
end
end
ngx.status = 502
ngx.say("Service unavailable after retries")
基于 upstream 动态健康状态做故障转移
当后端有多个节点(如 A/B/C),可结合 OpenResty 的共享字典(shared dict)记录各节点近期失败次数或响应时间,实现“软健康检查”。
- 定义 shared dict 存储节点状态:
lua_shared_dict backend_health 10m; - 每次请求前查字典,跳过标记为“不可用”的节点;请求失败后更新该节点失败计数
- 设定阈值(如连续 3 次失败)将节点置为临时下线,并设置自动恢复时间(TTL)
- 配合
balancer_by_lua*阶段,动态修改 upstream 分配逻辑(需启用lua_http10_buffering off)
注意:Nginx 原生 upstream 的 max_fails/fail_timeout 在 OpenResty 中仍生效,但 Lua 层控制更细粒度(例如按 HTTP 状态码区分失败)。
组合 upstream 与 Lua fallback 实现多级容灾
真实场景中常需“主 upstream → 备 upstream → 本地缓存/降级响应”三级策略:
- 第一层:用标准
upstream+proxy_pass转发,开启proxy_next_upstream error timeout http_502 http_503 http_504 - 第二层:在
error_page 502 503 504 = @fallback中进入 Lua 处理,尝试调用备用集群或降级接口 - 第三层:Lua 中读取
lua_shared_dict中的兜底数据(如预热的 JSON 缓存),或返回静态模板响应
这样既保留 Nginx 高性能转发能力,又赋予 Lua 层业务感知和智能决策能力。
关键注意事项
- 避免在重试逻辑中阻塞协程:所有 IO 操作必须用 cosocket(即
resty.http),禁用同步 socket 或 system 调用 - 共享字典操作要加锁(
shdict:lock/unlock)或用 CAS(incr+ 条件判断)防止并发冲突 - 重试次数不宜过多(建议 ≤3),尤其对外部 API,避免雪崩或被限流
- 记录日志时用
ngx.log(ngx.WARN, ...)而非print,并带上 trace_id 方便链路追踪


















