OpenResty通过Lua在access_by_lua_block阶段实时查Redis实现动态路由,无需重启即可秒级生效,支持灰度发布、AB测试等场景;核心是用内部location /redis封装查询,配合lua_shared_dict缓存与兜底策略保障高可用。

OpenResty 让 Nginx 真正具备运行时决策能力,而 Redis 作为轻量、高速的外部规则中心,是实现动态路由最常用也最稳妥的选择。整个流程不依赖重启,规则变更秒级生效,适合灰度发布、AB 测试、租户隔离等场景。
核心思路:用 Lua 查 Redis,再改 proxy_pass 目标
关键不在“怎么连 Redis”,而在“在哪查、怎么用、怎么兜底”。OpenResty 不把路由逻辑写死在配置里,而是把判断时机交给 Lua,在请求进入代理前实时决定发往哪个 upstream。
- location / 是入口,所有流量先到这里
- access_by_lua_block 阶段做路由决策(鉴权、参数解析、Redis 查询)
- 内部 location /redis 封装 Redis 查询,仅限内部调用,避免暴露
- ngx.var.target 存储查到的后端地址(如 svc-order-v2:8080),proxy_pass 直接引用变量
配置示例:按请求头 X-Env 切集群
假设你有 v1 和 v2 两套订单服务,希望根据请求头 X-Env: staging 或 X-Env: prod 自动转发,且规则存在 Redis 中:
- Redis key 为
route:order:$env,value 是上游地址(如http://10.0.1.10:8080) - Nginx 配置中不定义具体 server,只预留 upstream 占位符(或直接拼接 IP)
- Lua 脚本里加缓存(
lua_shared_dict),5 秒 TTL,减少 Redis 压力
片段示意:
lua_shared_dict route_cache 10m;
upstream dummy { server 127.0.0.1:1; } # 占位,实际由 proxy_pass 变量覆盖
<p>server {
listen 80;
location = /redis {
internal;
set_unescape_uri $key $arg_key;
redis2_query get $key;
redis2_pass 127.0.0.1:6379;
}</p><pre class="brush:php;toolbar:false;">location /api/order {
access_by_lua_block {
local env = ngx.req.get_headers()["X-Env"] or "prod"
local cache = ngx.shared.route_cache
local key = "route:order:" .. env
local cached = cache:get(key)
if cached then
ngx.var.upstream_target = cached
return
end
local res = ngx.location.capture("/redis", { args = { key = key } })
if res.status == 200 and res.body and #res.body > 0 then
local parser = require "redis.parser"
local addr, typ = parser.parse_reply(res.body)
if typ == parser.BULK_REPLY and addr then
cache:set(key, addr, 5) -- 缓存 5 秒
ngx.var.upstream_target = addr
return
end
end
ngx.var.upstream_target = "http://10.0.1.5:8080" -- 默认兜底
}
proxy_pass $upstream_target;
proxy_set_header Host $host;
}
}
增强可用性:加超时、错误降级与日志追踪
生产环境不能只靠“查得到就转发”,必须考虑 Redis 挂了、网络抖动、返回空值等情况:
- redis2_pass 后可加
redis2_connect_timeout 300ms和redis2_send_timeout 300ms - 所有
ngx.location.capture调用都应设timeout参数(单位毫秒) - 失败时记录 warn 日志:
ngx.log(ngx.WARN, "fallback to default: ", err) - 在 log_format 中加入
$upstream_target和$request_id,方便链路排查
进阶技巧:支持路径匹配 + 参数组合路由
比如 /api/user?tenant=a → 查 Redis key route:user:tenant-a;/api/report?format=csv → 查 route:report:format-csv。Lua 里可用 ngx.var.arg_tenant 或 ngx.req.get_uri_args() 提取参数,拼出精确 key。
- 避免硬编码路径拆分,优先用
ngx.var.uri和ngx.var.args - 对 key 做标准化处理(小写、去空格、截断过长值),防止 Redis 键爆炸
- 配合 Consul 或 etcd 时,可将 Redis 作为二级缓存,主数据源走服务注册中心











