核心是用access_by_lua_block在proxy_pass前实时决策路由,通过ngx.var.backend变量动态控制转发目标,结合redis查策略、shared_dict缓存、fallback兜底,无需reload。

用 Nginx + Lua 实现动态流量切分,核心是把路由决策从静态配置中“抽出来”,交由 Lua 脚本在请求时实时判断,并通过变量控制转发目标。不需要 reload,也不依赖 upstream 块硬编码,关键靠 OpenResty 提供的生命周期钩子和 Lua 扩展能力。
选对执行阶段:access_by_lua_block 是主力
流量切分逻辑必须在 proxy_pass 之前完成,否则已错过转发时机。推荐统一放在 access_by_lua_block 中,它能读取全部请求上下文(如参数、Header、Cookie、IP),也能安全设置 ngx.var.upstream_addr 或自定义变量(如 ngx.var.backend),供后续 proxy_pass 引用。
- 避免用 content_by_lua_block —— 它在 proxy 之后执行,无法影响本次转发
- 慎用 set_by_lua_block —— 仅适合轻量计算(如字符串截取、取模),不支持 Redis 查询或复杂判断
- balancer_by_lua_block 更适合节点级负载均衡,而非业务维度的流量分发(如灰度、AB、机房路由)
提取分流依据:灵活适配多种业务信号
根据实际场景选择稳定、可控、易识别的分流标识,Lua 可直接解析:
- URL 路径:用 string.match(ngx.var.uri, "/api/v(.-)/") 提取版本号,v1→老集群,v2→新集群
- GET 参数:如 ngx.var.arg_env == "gray" 或 tonumber(ngx.var.arg_uid) % 100 (5% 用户灰度)
- Header 或 Cookie:如 ngx.req.get_headers()["X-Release-Phase"] == "beta",或 ngx.var.cookie_trace_id
- 客户端 IP:用 ngx.var.remote_addr 或更准的 ngx.req.get_headers()["X-Real-IP"],查 Redis 白名单做 IP 切流
对接动态数据源:Redis + 共享字典兜底
规则不应写死在配置里。典型做法是把分流策略存在 Redis,Lua 运行时查询,再用 lua_shared_dict 缓存结果提升性能与容灾能力:
- 在 http 块中声明共享缓存:lua_shared_dict route_cache 10m;
- 在 access_by_lua_block 中:先查共享字典,命中则直接用;未命中再查 Redis(设超时 ≤100ms、重试 1 次)
- Redis 返回空或失败时,返回缓存中的 last_known 值;全不可用则 fallback 到默认 upstream(如 proxy_pass http://default_backend;)
配置 proxy_pass 动态代理
最终转发必须使用变量,Nginx 才能解析为真实地址:
- 在 access_by_lua_block 中设置:ngx.var.backend = "192.168.1.10:8080" 或 ngx.var.backend = "new-cluster"(需配合 resolver)
- location 块中写:proxy_pass http://$backend;(注意有 $ 符号,且不能加斜杠结尾)
- 若 backend 是域名,需提前配置 resolver 8.8.8.8 valid=30s;,否则解析失败











