1. 开篇:当Lua遇见Nginx变量
作为OpenResty生态的核心要素,Lua与Nginx的变量系统结合堪称网关开发的瑞士军刀。本文将从实际案例出发,带您掌握在请求处理各个阶段操作变量的"正确姿势"。
2. 内置变量的妙用指南
Nginx原生提供的变量如同乐高积木的基础模块,这里以OpenResty 1.21.4技术栈为例演示常用场景:
2.1 请求属性获取
location /debug {
access_by_lua_block {
-- 输出完整请求路径(含参数)
ngx.log(ngx.INFO, "请求URI:", ngx.var.request_uri)
-- 解析客户端连接属性
local client_ip = ngx.var.remote_addr
local keepalive = ngx.var.keepalive_requests
ngx.header["X-Client-IP"] = client_ip
}
}
2.2 流量管控实战
location /api {
access_by_lua_block {
-- 读取请求速率计数器
local req_count = tonumber(ngx.var.connections_active) or 0
if req_count > 100 then
ngx.exit(503) -- 触发过载保护
end
-- 动态设置缓冲大小
ngx.var.limit_rate = "100k" -- 限速到100KB/s
}
}
3. 自定义变量的进阶玩法
通过Lua语言扩展变量系统,可以实现传统配置难以企及的灵活逻辑。
3.1 变量声明三步曲
server {
set_by_lua_block $dynamic_var {
-- 初始化阶段创建变量
local math = require("math")
return math.random(1000) -- 生成随机标识
}
location /custom {
rewrite_by_lua_block {
-- 动态修改变量值
ngx.var.dynamic_var = "user_" .. ngx.var.http_user_id
-- 使用共享字典保持状态
local shdict = ngx.shared.my_cache
shdict:set("last_request", ngx.time())
}
content_by_lua_block {
-- 输出复合变量
ngx.say("当前会话标识:", ngx.var.dynamic_var)
}
}
}
3.2 变量的生命周期掌控
通过不同阶段的变量操作实现流水线处理:
location /flow {
set $phase "init"; -- 配置阶段初始化
access_by_lua_block {
ngx.var.phase = "auth" -- 访问控制阶段
local auth_code = validate_token()
ngx.var.auth_code = auth_code
}
header_filter_by_lua_block {
ngx.var.phase = "header" -- 响应头处理阶段
ngx.header["X-Phase"] = ngx.var.phase
}
}
4. 与关联技术的协同工作
结合Redis实现带缓存的用户状态管理:
location /profile {
access_by_lua_block {
local redis = require("resty.redis")
local red = redis:new()
-- 从变量获取用户ID
local uid = ngx.var.cookie_user_id
-- 查询Redis缓存
local res, err = red:get("user:" .. uid)
if not res then
ngx.log(ngx.ERR, "Redis查询失败:", err)
return
end
-- 注入变量供后续使用
ngx.var.user_profile = res
}
content_by_lua_block {
ngx.say("用户资料:", ngx.var.user_profile)
}
}
5. 性能优化的关键要点
5.1 存储方式的三级火箭
-- 方式1:直接操作Nginx变量(最慢但兼容性好)
ngx.var.custom_value = "data"
-- 方式2:使用ngx.ctx上下文(速度提升3倍)
ngx.ctx.cached_data = expensive_calculation()
-- 方式3:模块级变量(最快但需注意生命周期)
local _cache = {}
function get_cached_value(key)
return _cache[key] or fetch_from_db(key)
end
5.2 内存管理原则
location /memcheck {
content_by_lua_block {
-- 错误示范:大对象存储在变量
local big_data = string.rep("A", 1024000) -- 1MB数据
ngx.var.big_var = big_data -- 触发内存暴涨
-- 正确做法:使用共享字典
local shdict = ngx.shared.temp_storage
shdict:set("temp_key", big_data, 60) -- 限制存储时间
}
}
6. 实战中的避坑指南
6.1 变量作用域陷阱
location /scope {
set_by_lua_block $counter {
-- 此代码仅在配置加载时执行一次!
local count = 0
count = count + 1
return count
}
content_by_lua_block {
ngx.say("当前计数:", ngx.var.counter) -- 永远输出1
}
}
改用共享字典实现真正计数:
set_by_lua_block $counter {
local shdict = ngx.shared.counter_store
return shdict:incr("global_counter", 1, 0)
}
6.2 特殊字符处理
location /escape {
content_by_lua_block {
local raw_value = "含有空格&特殊字符"
ngx.var.encoded_value = ngx.escape_uri(raw_value)
-- 输出:%E5%90%AB%E6%9C%89%E7%A9%BA%E6%A0%BC%26%E7%89%B9%E6%AE%8A
}
}
7. 总结与最佳实践
应用场景:
- 动态路由决策(基于变量值的A/B测试)
- 请求属性透传(跨阶段数据传递)
- 实时监控指标(流量统计与限流)
技术选型建议:
- 优先使用内置变量完成基础功能
- 关键路径避免高频变量操作
- 复杂逻辑建议使用ngx.ctx代替临时变量
注意事项检查表:
- [ ] 是否在热代码路径操作变量?
- [ ] 变量命名是否可能冲突?
- [ ] 超大值是否考虑存储方式?
- [ ] 是否需要URI编解码处理?
评论