在 nginx 中结合 lua 脚本实现动态路由,核心做法是在 location 块中使用 content_by_lua_block 拦截请求,通过 lua api 解析 url 或 header 获取目标服务标识,然后调用 ngx.exec() 将请求内部重定向到对应的 nginx 静态路由规则。这样做可以绕过 nginx 原生配置的限制,根据业务逻辑(如用户 id、租户 id 或时间)实时计算并分发流量,且无需重启 nginx 即可生效。

如何编写 Lua 脚本以解析路径并触发重定向
要实现这一功能,需要在 Nginx 配置中定义一个通用的入口 location,并在其中嵌入 Lua 代码。Lua 脚本负责提取请求中的关键参数,例如从 URI 中提取版本号或租户 ID,然后根据这些参数决定转发给哪个后端服务组。关键在于使用 ngx.exec() 而不是 ngx.redirect(),前者是内部跳转,保持客户端 URL 不变,适合做透明路由。

server {
listen 80;
server_name example.com;
# 定义后端服务组
upstream backend_v1 {
server 127.0.0.1:8080;
}
upstream backend_v2 {
server 127.0.0.1:8081;
}
location /api/ {
default_type text/plain;
content_by_lua_block {
local uri = ngx.var.uri
-- 假设 URI 格式为 /api/v1/resource 或 /api/v2/resource
local version = string.match(uri, "/api/(v%d+)/")
if version == "v1" then
ngx.exec("@backend_v1", uri)
elseif version == "v2" then
ngx.exec("@backend_v2", uri)
else
ngx.status = 404
ngx.say("Unknown version")
end
}
}
location @backend_v1 {
proxy_pass http://backend_v1;
}
location @backend_v2 {
proxy_pass http://backend_v2;
}
}
上述配置中,当请求到达 /api/ 时,Lua 脚本会匹配 v1 或 v2。如果匹配到 v1,则执行 ngx.exec("@backend_v1", uri),Nginx 会在内部查找名为 @backend_v1 的 location 块,并将请求代理到 upstream backend_v1。这种机制允许在不暴露内部结构的情况下,根据动态条件路由请求。
如何利用 Header 信息实现基于租户的动态路由
除了路径解析,另一种常见的动态路由场景是基于 HTTP Header 进行多租户隔离。在这种情况下,Lua 脚本需要读取自定义 Header(如 X-Tenant-ID),并根据该 ID 映射到特定的后端服务器。这种方法适用于 SaaS 平台,不同租户的数据或服务实例完全隔离。

server {
listen 80;
server_name saas.example.com;
# 模拟租户与后端的映射表
local tenant_map = {
["tenant_a"] = "http://backend_tenant_a",
["tenant_b"] = "http://backend_tenant_b",
}
location / {
default_type application/json;
content_by_lua_block {
local tenant_id = ngx.var.http_x_tenant_id
local target = tenant_map[tenant_id]
if target then
-- 设置内部变量供 proxy_pass 使用
ngx.var.proxy_target = target
ngx.exec("@dynamic_proxy")
else
ngx.status = 403
ngx.say("{".."\"error\": \"Tenant not found\"}".."}")
end
}
}
location @dynamic_proxy {
proxy_pass $proxy_target;
}
}
在这个示例中,Lua 脚本首先检查 X-Tenant-ID Header。如果找到匹配的租户,它将目标 URL 存入 Nginx 变量 $proxy_target,然后通过 ngx.exec 跳转到 @dynamic_proxy 位置。在 @dynamic_proxy 中,proxy_pass 直接使用这个变量,从而实现动态指向。这种方式比硬编码多个 location 更灵活,因为新增租户只需更新 Lua 表或外部配置源,无需修改 Nginx 主配置。
当 Lua 脚本中未找到匹配的租户 ID 时会返回什么状态码
在基于 Header 的动态路由实现中,如果请求中缺少 X-Tenant-ID Header 或者提供的 ID 不在预定义的映射表中,Lua 脚本会进入错误处理分支。此时,脚本会显式设置 HTTP 状态码为 403 Forbidden,并返回一段 JSON 格式的文本说明错误原因。这确保了非法或未授权的请求不会泄露任何内部路由信息,而是被直接拒绝。



















