OpenResty + Lua 应在原生 Nginx 配置难以维护时引入:rewrite_by_lua 替代易错的 if+set+rewrite;balancer_by_lua 实现动态负载均衡;access_by_lua 支持灵活鉴权与限流;log_by_lua 补全日志字段并利用 ngx.ctx 串联状态。

没必要为迁而迁;只有当原生 Nginx 配置开始“写不下去”或“改不动”时,才是 OpenResty + Lua 的入场时机。
rewrite_by_lua* 替代 if + set + rewrite 组合
原生 Nginx 的 if 指令语义模糊、嵌套行为不可靠,配合 set 和 rewrite 容易触发非预期跳转或变量覆盖。比如想根据请求头值重写 URI,常见写法会漏掉空值判断、正则捕获失败分支,最终返回 404 或循环重定向。
- 用
rewrite_by_lua_block直接读取ngx.var.http_x_version,做字符串匹配或 tonumber 转换,逻辑清晰可控 - 避免
if ($http_x_version ~* "v2") { rewrite ^(.*)$ /api/v2$1 break; }这类易出错的链式配置 - 注意:Lua 中的 URI 修改必须调用
ngx.req.set_uri()或ngx.req.set_uri_args(),不能只改变量
balancer_by_lua* 绕过静态 upstream 限制
当后端节点需要按秒级变化(如服务发现刷新、灰度组动态增删)、或需结合业务字段(如 ngx.var.arg_uid)做一致性哈希时,upstream 块里硬编码 server 行就彻底失效了。
-
balancer_by_lua_block内可调用balancer.set_current_peer("10.0.1.5", 8080)手动指定目标,完全跳过 upstream 解析流程 - 节点列表建议从
shared_dict读取,而非每次 Redis 查询——高频场景下 cosocket 调用可能打满连接池 - 务必在
balancer_by_lua_block开头加超时防护:if ngx.now() - ngx.var.time_iso8601 > 0.1 then return end
access_by_lua* 实现轻量级鉴权与限流
原生 auth_request 依赖子请求、开销大;limit_req 只支持简单 key,无法按用户等级或 API 路径差异化限流。
- 用
access_by_lua_block调用resty.limit.count:new(),key 可拼接ngx.var.remote_addr .. ":" .. ngx.var.arg_appid - 鉴权失败直接
ngx.exit(403),避免透传到后端再返回错误 - 慎用
cosocket同步调用外部认证服务——worker 进程会阻塞;优先走本地缓存或异步 timer 预热
log_by_lua* 补足原生日志字段缺失
Nginx 日志变量无法获取 upstream 响应体长度、自定义 header、或 Lua 计算中间值(如路由决策耗时),log_format 扩展能力极其有限。
-
log_by_lua_block可访问ngx.ctx中 rewrite/access 阶段存入的上下文,比如ngx.ctx.route_time - 聚合指标推荐写入
shared_dict,再由定时器推送到 Pushgateway;避免每请求都发 HTTP - 注意:log 阶段不能调用
ngx.exit或修改响应,仅用于记录和副作用
真正难处理的不是语法转换,而是把原来分散在多个配置块里的隐式状态(比如 if 判断结果、rewrite 后的 URI、access 阶段的鉴权标记)显式地用 ngx.ctx 或 shared_dict 串起来。这个状态流转设计没理清,Lua 代码反而比原生配置更难 debug。











