nginx中使用openresty和balancer_by_lua*实现动态负载均衡路由,通过共享字典缓存节点、服务发现集成及lua选点逻辑(如参数/哈希/健康状态)动态设定后端目标,需规避阻塞操作并妥善管理连接与状态。

在 Nginx 中使用 Lua 实现动态负载均衡路由,核心是借助 OpenResty(Nginx + Lua 运行时)和 lua-resty-upstream 或自定义逻辑,在请求处理阶段(如 access_by_lua* 或 balancer_by_lua*)动态决定将请求转发到哪个上游节点。
1. 前提:安装 OpenResty 并启用 Lua 支持
标准 Nginx 不支持原生 Lua,必须使用 OpenResty(推荐)或编译 Nginx 时添加 --with-http_lua_module。确认已安装后,在配置中启用 Lua 指令:
- 确保
lua_package_path设置正确(如需加载自定义 Lua 模块) - 在
http块中添加lua_shared_dict upstream_cache 10m;(用于缓存上游列表或权重) - 避免在高并发场景下频繁读写文件或全局变量,优先用共享字典(
shared_dict)或内存表
2. 使用 balancer_by_lua* 实现真正动态选点
这是最灵活、最推荐的方式——它在 Nginx 的 upstream 负载均衡阶段介入,可完全绕过静态 upstream 块,直接控制 socket 连接目标。需配合 resolver 和 set $backend "" 占位符使用:
- 定义一个无 server 的 upstream(仅作占位):
upstream dynamic_backend { balancer_by_lua_block { /* Lua 选点逻辑 */ } } - 在
balancer_by_lua_block中调用balancer.set_current_peer(ip, port)指定目标 - 可通过请求头(
ngx.var.http_x_target)、URL 参数(ngx.var.arg_env)、IP 地址哈希、服务发现 API(如 Consul/etcd)或 Redis 查询实时获取节点列表 - 示例逻辑:根据 URL 中的
env=prod参数,从共享字典查出 prod 组可用节点,按轮询或一致性哈希选取
3. 结合服务发现实现自动扩缩容感知
动态路由的价值在于响应后端变化。可让 Lua 脚本定期或按需拉取最新节点列表:
- 用
lua-resty-http或lua-resty-consul模块请求注册中心 API 获取健康实例 - 结果存入
lua_shared_dict,并设置 TTL(如 30 秒),避免每次请求都调用 API - 在
balancer_by_lua_block中先查缓存,失效则触发异步更新(注意:balancer 阶段不能阻塞,更新建议放在timer_at或 access 阶段预热) - 若某节点连续失败,可在 Lua 中标记为“临时不可用”,跳过选择(需自己维护状态,Nginx 不自动剔除)
4. 注意事项与常见陷阱
动态 Lua 路由虽强大,但容易引入性能和稳定性问题:
- 不要在 balancer_by_lua 中做耗时操作:如同步 HTTP 请求、文件 IO、复杂计算——会阻塞 worker 进程
-
避免全局变量存储状态:不同请求共享同一 Lua VM,用
ngx.ctx(单请求生命周期)或shared_dict(跨请求共享)代替 -
错误处理必须完备:Lua 报错会导致 500;务必用
pcall包裹关键逻辑,并 fallback 到默认节点 -
连接复用需手动管理:使用
balancer_by_lua后,Nginx 不再自动管理 upstream 连接池,需自行控制 keepalive(可结合lua-resty-upstream)











