Nginx动态路由核心是在请求时实时决定转发目标,而非修改静态配置;proxy_pass不支持直接变量IP:Port,须通过balancer_by_lua_block接管或映射为预定义upstream名。

能实现,但必须绕开 upstream 块的静态定义限制——动态路由的核心不是“改配置”,而是“在请求时实时决定转发目标”。
为什么不能直接用 proxy_pass http://$var 绑定 Redis 查出来的地址
因为 Nginx 的 proxy_pass 变量解析只支持预定义的 upstream 名称,不支持任意 IP:Port 字符串(如 proxy_pass http://10.0.1.5:8080)。硬写会报错 invalid URL prefix。
- 真正可行的路径是:用
balancer_by_lua_block在负载均衡阶段接管连接选择,跳过upstream块解析 - 或者退一步,在
rewrite_by_lua_block中把 Redis 返回的host:port映射为一个已声明的upstream名(如svc-order-v2),再走proxy_pass http://$target_upstream - 后者更轻量、兼容性更好,适合大多数灰度/AB 场景;前者更灵活,但需 OpenResty ≥ 1.21.4 且逻辑稍重
access_by_lua_block 里查 Redis 路由前,必须先做三件事
否则高并发下会打爆 Redis 或拖慢整个请求链路。
- 用
lua_shared_dict缓存路由结果,TTL 设为 5–10 秒(local dict = ngx.shared.route_cache; dict:set(key, value, 5)) - 查缓存失败时,才用
resty.redis发起查询,并严格设置超时:red:set_timeout(100)(单位毫秒) - Redis 查询失败或超时,必须有兜底行为:返回上一次有效缓存值,或 fallback 到默认
upstream(如backend_default),绝不能直接ngx.exit(502)
从请求中提取服务名的三种可靠方式及对应风险
服务名是查 Redis 路由规则的 key,提取不准就完全路由错。
-
Host 头提取:适用于子域名隔离(如
order.api.example.com→order)。注意 Host 可被伪造,生产环境需配合valid_referers或 TLS SNI 校验 -
URL 路径前缀:如
/payment/xxx→payment。需提前用map指令做路径到服务名的映射,避免每次 Lua 解析字符串 -
Header 指定:如
X-Service-Name: user。最灵活,但依赖上游调用方配合;若 header 为空,必须设默认 fallback,否则 Lua 报attempt to concatenate a nil value
Redis 路由数据格式和更新接口怎么设计才不踩坑
别用模糊匹配或嵌套 JSON——Lua 解析成本高,且难以原子更新。
- 推荐扁平键值结构:
route:api:user→http://svc-user-v2:8080,或route:env:staging→backend_staging - 更新接口必须带版本号或时间戳字段,避免多实例并发写入覆盖(例如用
SET route:api:user "http://v2" NX EX 3600) - 禁止在 Lua 中做正则匹配或通配符查找(如
KEYS route:api:*),会阻塞 Redis 单线程,改用业务层预计算 + 多 key 写入
最易被忽略的一点:所有 Lua 中构造的 Redis key 都要经过 ngx.escape_uri 或白名单过滤,否则恶意请求如 ?service=..%2fetc%2fpasswd 可能触发路径遍历或命令注入(即使 Redis 不支持,也防未来模块升级风险)。











