在微服务与云原生架构日益普及的今天,API 网关作为流量的统一入口,承担着路由转发、鉴权、限流、日志记录等关键职责。传统的 Nginx 配置虽然强大,但在面对动态、细粒度的业务逻辑时,往往需要频繁 reload 且难以编写复杂逻辑。而 ngx_lua 模块的出现,让 Nginx 拥有了脚本级编程能力,成为构建高性能动态网关的理想选择。
为什么选择 Nginx + Lua 构建网关
Nginx 本身具备高并发、低内存占用、事件驱动等特性,而 LuaJIT 的引入带来了接近 C 语言的执行效率。两者结合,可以在不牺牲性能的前提下实现:
- 动态路由:根据请求头、参数、路径实时决定上游服务。
- 细粒度鉴权:JWT 校验、签名验证、IP 黑白名单。
- 限流与熔断:基于令牌桶或漏桶算法,在 Lua 层快速拒绝过载请求。
- 响应改写:动态修改响应头、响应体,甚至聚合多个后端接口。
- 可观测性:实时上报日志、指标到 Prometheus 或 Kafka。
与 OpenResty 相比,原生 Nginx 编译 ngx_lua 模块同样可行,且更轻量。下面我们从环境搭建开始,逐步实现一个动态网关的核心功能。
环境准备与模块编译
假设你使用 Ubuntu 22.04,推荐直接使用 OpenResty,它集成了 Nginx、LuaJIT、ngx_lua 及大量常用 Lua 库。若坚持原生 Nginx,则需下载 lua-nginx-module、ngx_devel_kit 并重新编译 Nginx。
# 安装 OpenResty
wget https://openresty.org/package/ubuntu/pool/main/o/openresty/openresty_1.21.4.1_amd64.deb
sudo dpkg -i openresty_1.21.4.1_amd64.deb
安装完成后,/usr/local/openresty/nginx/sbin/nginx 即为可执行文件。在 nginx.conf 中通过 content_by_lua_block 或 access_by_lua_file 嵌入 Lua 代码。
核心指令与执行阶段
理解 Nginx 的请求处理阶段是编写 Lua 网关的关键。ngx_lua 提供了多个指令,对应不同阶段:
init_by_lua_block:Master 进程启动时执行,用于加载全局配置、预编译正则。init_worker_by_lua_block:每个 Worker 启动时执行,适合启动定时器、连接池。set_by_lua_block:设置变量,可用于简单计算。rewrite_by_lua_block:重写阶段,适合动态路由改写。access_by_lua_block:访问控制,鉴权、限流的最佳位置。content_by_lua_block:生成响应内容,适合直接返回 JSON。log_by_lua_block:日志阶段,异步上报。
一个典型的动态网关会在 access_by_lua_file 中完成鉴权与限流,在 balancer_by_lua_block 中动态选择上游节点。
实战:动态路由与鉴权网关
下面实现一个简化网关:根据请求头 X-Service 将请求转发到不同后端,并校验 Authorization 中的 JWT。
1. 配置结构
http {
lua_package_path "/etc/nginx/lua/?.lua;;";
lua_shared_dict jwt_cache 10m;
init_by_lua_block {
local cjson = require "cjson"
-- 预加载配置
}
server {
listen 8080;
location /api/ {
access_by_lua_file /etc/nginx/lua/auth.lua;
proxy_pass http://backend;
}
}
upstream backend {
server 0.0.0.0; # 占位,由 balancer_by_lua 动态选择
balancer_by_lua_file /etc/nginx/lua/balancer.lua;
}
}
2. 鉴权 Lua 脚本 auth.lua
local cjson = require "cjson"
local jwt = require "resty.jwt"
-- 从 Header 获取 Token
local auth_header = ngx.req.get_headers()["Authorization"]
if not auth_header or not auth_header:match("Bearer%s+(.+)") then
ngx.status = 401
ngx.say(cjson.encode({error = "missing token"}))
return ngx.exit(401)
end
local token = auth_header:match("Bearer%s+(.+)")
local jwt_obj = jwt:verify("your-secret", token)
if not jwt_obj.verified then
ngx.status = 403
ngx.say(cjson.encode({error = "invalid token"}))
return ngx.exit(403)
end
-- 将用户信息注入请求头,供后端使用
ngx.req.set_header("X-User-Id", jwt_obj.payload.sub)
3. 动态负载均衡 balancer.lua
local upstream = require "ngx.upstream"
local balancer = require "ngx.balancer"
-- 根据 X-Service 选择后端池
local service = ngx.var.http_x_service or "default"
local backends = {
user = {{host="10.0.0.1", port=8001}, {host="10.0.0.2", port=8001}},
order = {{host="10.0.0.3", port=8002}},
default = {{host="10.0.0.9", port=9000}}
}
local pool = backends[service] or backends.default
-- 简单轮询(生产可用一致性哈希)
local idx = ngx.worker.id() % #pool + 1
local target = pool[idx]
local ok, err = balancer.set_current_peer(target.host, target.port)
if not ok then
ngx.log(ngx.ERR, "failed to set peer: ", err)
return ngx.exit(502)
end
4. 限流保护
利用 lua_shared_dict 和 resty.limit.req 实现令牌桶限流:
local limit_req = require "resty.limit.req"
local limiter = limit_req.new("jwt_cache", 100, 50) -- 100r/s,突发50
local key = ngx.var.binary_remote_addr
local delay, err = limiter:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503)
end
ngx.log(ngx.ERR, "limit error: ", err)
return ngx.exit(500)
end
if delay > 0 then
ngx.sleep(delay)
end
性能优化与注意事项
- 避免阻塞操作:Lua 代码运行在 Nginx Worker 中,任何阻塞(如同步 HTTP 请求、磁盘 IO)都会拖慢整个 Worker。务必使用
ngx.location.capture或resty.http的异步接口。 - 共享内存字典:
lua_shared_dict是跨 Worker 共享数据的唯一方式,适合缓存 JWT、限流计数器。但注意其大小有限,且操作是原子的但非事务性。 - 代码缓存:使用
lua_code_cache on(默认开启),生产环境切勿关闭,否则每次请求都会重新编译 Lua。 - 错误处理:始终用
pcall包裹可能出错的逻辑,并通过ngx.log记录到 error.log。 - 连接池:对于后端 HTTP 调用,使用
resty.http的连接池,避免频繁握手。
总结
Nginx 与 Lua 的结合,让网关从静态配置进化为可编程的动态服务。通过 access_by_lua、balancer_by_lua 等阶段指令,我们可以在毫秒级内完成鉴权、路由、限流等操作,同时保持 Nginx 的高并发优势。本文展示的示例虽简,但已涵盖动态网关的核心骨架。生产环境中,你还可以集成 Consul 做服务发现、用 Redis 做分布式限流、通过 OpenTracing 做链路追踪。掌握 ngx_lua,等于为你的微服务架构装上了一扇智能、高效的大门。
未经允许不得转载:任鹏个人博客 » Nginx 与 Lua 模块开发:构建动态网关服务


朋友圈点赞图在线生成源码