OpenResty 中 Lua 脚本实战示例(分场景讲清 Lua 解决了 Nginx 原生做不到的事)

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 的核心能力汇总

  1. 可编程鉴权:读取 Redis/JWT 实现登录校验、权限控制
  2. 动态路由网关:灰度发布、多环境转发、流量染色
  3. 独立接口开发:无需 Java/Go 后端,直接用 Lua 写 HTTP 服务
  4. 灵活限流防护:按 IP / 用户自定义限流规则,防刷接口
  5. 响应动态篡改:修改返回头、替换页面、脱敏返回数据
  6. 后台异步任务:定时刷新缓存、上报监控,不阻塞业务请求
  7. 接口聚合:一次请求调用多个后端,合并结果返回前端
  8. 自定义日志监控:结构化日志、异步上报链路追踪数据

简单一句话:Nginx 负责高性能网络收发,Lua 负责所有复杂业务逻辑

相关推荐
帅次1 小时前
Android 应用高级面试:Window 近1年高频追问 18 题
android·面试·职场和发展
Revolution612 小时前
Node.js 是什么:前端项目里哪些事情由它完成
前端·面试·node.js
GitLqr16 小时前
深入理解 Flutter 架构:从 Widget 到 GPU 像素的全链路解析
flutter·面试·dart
众人皆醒我独醉18 小时前
TGI:HuggingFace 的官方推理服务——不止 PagedAttention,更懂模型生态
面试·llm·ai编程
众人皆醒我独醉18 小时前
vLLM:PagedAttention 如何让 LLM 推理吞吐提升 24 倍
面试·llm·ai编程
神奇小汤圆19 小时前
面试官突然问:“数仓哪一层最适合做RAG?”他一下被问住了…
面试
一只旭宝20 小时前
C++手写shared_ptr共享智能指针|原子引用计数、强弱引用控制块、赋值重载底层深度剖析
开发语言·c++·面试
神奇小汤圆21 小时前
工业级 RAG 为什么需要 Elasticsearch?一篇讲清索引、度量、过滤与 Milvus 选型
面试
swipe1 天前
14|(前端转全栈)商品详情高频访问怎么扛?Redis Cache Aside 实战
前端·后端·面试