Nginx 原生配置只有静态指令,逻辑极弱;Lua 给 Nginx 赋予可编程能力,下面分 8 个高频场景,附完整可运行代码,讲清 Lua 发挥的作用。
前置基础
所有 Lua 代码写在 xxx_by_lua_block {} 里,运行在 Nginx 不同生命周期阶段。
示例 1:access_by_lua 实现接口鉴权(最常用)
Lua 作用
原生 Nginx 只能简单 IP 放行,无法读取 Header Token、校验 JWT、读取 Redis 判断登录状态,Lua 可以实现完整鉴权逻辑。
nginx
bash
http {
# 加载redis客户端库
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
server {
listen 80;
location /api {
access_by_lua_block {
-- 1. 获取请求头token
local token = ngx.var.http_Authorization
if not token then
ngx.print('{"code":401,"msg":"未携带token"}')
return ngx.exit(401)
end
-- 2. 连接Redis校验token是否有效
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.exit(500)
end
local user_id, err = red:get(token)
if not user_id or user_id == ngx.null then
ngx.print('{"code":401,"msg":"token失效"}')
return ngx.exit(401)
end
-- 3. 把用户ID传递给后端服务
ngx.var.user_id = user_id
}
proxy_pass http://backend_server;
proxy_set_header X-User-ID $user_id;
}
}
}
核心价值
动态鉴权、读取缓存、变量透传,纯 Nginx 配置无法实现。
示例 2:rewrite_by_lua 动态路由分发
Lua 作用
原生 rewrite 正则固定写死,Lua 可根据请求参数、Redis 配置、灰度规则动态转发不同后端。
nginx
bash
location /gateway {
rewrite_by_lua_block {
local args = ngx.req.get_uri_args()
local env = args.env or "prod"
-- 根据参数动态修改转发地址
if env == "test" then
ngx.var.backend = "http://127.0.0.1:8081"
else
ngx.var.backend = "http://127.0.0.1:8080"
end
}
proxy_pass $backend;
}
示例 3:content_by_lua 直接写接口,不用后端服务
Lua 作用
Nginx 原生只能返回静态文件,Lua 可独立编写 HTTP 接口,直接返回 JSON,适合简单验证码、健康检查、配置接口。
nginx
bash
location /health {
content_by_lua_block {
local resp = {
code = 0,
msg = "服务正常",
time = ngx.time()
}
-- 返回json
ngx.header["Content-Type"] = "application/json"
ngx.print(require("cjson").encode(resp))
}
}
示例 4:ngx.shared.DICT + Lua 实现单机限流
Lua 作用
原生限流模块只能按固定速率,Lua 可自定义限流逻辑:按用户 ID、接口、IP 分别计数,灵活控制。
nginx
bash
http {
# 开辟共享内存,所有worker进程共享
lua_shared_dict limit_store 10m;
server {
listen 80;
location /api {
access_by_lua_block {
local limit_store = ngx.shared.limit_store
local ip = ngx.var.remote_addr
local key = "limit:" .. ip
-- 计数器+1
local count, err = limit_store:incr(key, 1, 0, 60) -- 60秒过期
if count > 10 then -- 1分钟最多10次请求
ngx.print('{"code":429,"msg":"请求过于频繁"}')
return ngx.exit(429)
end
}
proxy_pass http://backend;
}
}
}
示例 5:header_filter_by_lua /body_filter_by_lua 修改响应内容
Lua 作用
原生 Nginx 很难动态修改后端返回的响应头、页面内容,Lua 可以拦截响应并篡改。
nginx
bash
location / {
proxy_pass http://backend;
-- 修改响应头
header_filter_by_lua_block {
-- 删除服务版本号,隐藏后端信息
ngx.header["Server"] = "gateway"
ngx.header["X-Powered-By"] = nil
}
-- 修改返回的body文本
body_filter_by_lua_block {
local data = ngx.arg[1]
if data then
-- 替换页面中的敏感文本
data = ngx.re.gsub(data, "后端真实服务名称", "统一网关服务")
ngx.arg[1] = data
end
}
}
示例 6:init_worker_by_lua 后台定时任务
Lua 作用
Nginx 本身没有定时任务能力,Lua 结合 ngx.timer.at 实现异步后台任务,不阻塞请求。
nginx
bash
http {
lua_shared_dict task_cache 5m;
init_worker_by_lua_block {
local function loop_task(premature)
if premature then return end
-- 每30秒执行一次:上报监控、刷新本地缓存
local redis = require "resty.redis"
-- 刷新配置缓存逻辑省略
-- 循环创建定时器
local ok, err = ngx.timer.at(30, loop_task)
end
-- 启动定时器
ngx.timer.at(0, loop_task)
}
}
示例 7:ngx.location.capture 子请求,接口聚合
Lua 作用
一个前端请求需要调用多个后端接口时,原生 Nginx 无法并发调用并合并结果;Lua 子请求串行 / 并发调用多个接口,组装统一 JSON 返回。
nginx
bash
location /agg {
content_by_lua_block {
local cjson = require "cjson"
-- 并发调用两个后端接口
local res1 = ngx.location.capture("/user/info")
local res2 = ngx.location.capture("/user/order")
-- 组装数据统一返回
local result = {
user = cjson.decode(res1.body),
order = cjson.decode(res2.body)
}
ngx.header["Content-Type"] = "application/json"
ngx.print(cjson.encode(result))
}
}
location /user/info { proxy_pass http://user-service; }
location /user/order { proxy_pass http://order-service; }
示例 8:log_by_lua 自定义日志、数据上报
Lua 作用
原生 access.log 格式固定,Lua 可在请求结束后读取完整请求 / 响应信息,格式化 JSON 日志,异步上报到 ELK、监控平台。
nginx
bash
location /api {
proxy_pass http://backend;
log_by_lua_block {
local log_data = {
ip = ngx.var.remote_addr,
uri = ngx.var.request_uri,
status = ngx.var.status,
cost = ngx.var.request_time,
token = ngx.var.http_Authorization
}
-- 异步上报日志(不会阻塞请求返回)
ngx.timer.at(0, function()
local http = require "resty.http"
local httpc = http.new()
httpc:request_uri("http://log-collector/log", {
method = "POST",
body = require("cjson").encode(log_data)
})
end)
}
}
总结:Lua 在 OpenResty 的核心能力汇总
- 可编程鉴权:读取 Redis/JWT 实现登录校验、权限控制
- 动态路由网关:灰度发布、多环境转发、流量染色
- 独立接口开发:无需 Java/Go 后端,直接用 Lua 写 HTTP 服务
- 灵活限流防护:按 IP / 用户自定义限流规则,防刷接口
- 响应动态篡改:修改返回头、替换页面、脱敏返回数据
- 后台异步任务:定时刷新缓存、上报监控,不阻塞业务请求
- 接口聚合:一次请求调用多个后端,合并结果返回前端
- 自定义日志监控:结构化日志、异步上报链路追踪数据
简单一句话:Nginx 负责高性能网络收发,Lua 负责所有复杂业务逻辑。