一、为什么需要监控Lua执行时间?
当你的Web应用突然响应变慢时,就像快递仓库突然堆积了上千个包裹,每个分拣工(Nginx worker)都在拼命工作却效率低下。在OpenResty架构中,Lua脚本就是这些分拣工的操作手册,它们的执行效率直接决定了整个系统的吞吐能力。
最近我遇到一个真实案例:某电商平台的商品详情页接口在促销期间频繁超时。通过监控发现,某个Lua脚本在流量高峰时平均执行时间从5ms暴增到200ms,最终定位到是未优化的JSON序列化代码导致。这充分说明执行时间监控的重要性。
二、监控原理与技术栈选择
2.1 OpenResty计时核心机制
OpenResty基于LuaJIT的时间函数提供了两种精度:
-- 毫秒级精度(适合常规监控)
local start_time = ngx.now() * 1000 -- 转换为毫秒
-- 微秒级精度(需要高精度场景)
local start_time = ngx.now() * 1000000 -- 转换为微秒
2.2 推荐技术栈组合
本文采用以下技术方案:
- OpenResty 1.21.4.1
- LuaJIT 2.1.0-beta3
- lua-resty-core 0.1.24
- lua-resty-lrucache 0.13
三、四种实战监控方案
3.1 基础版:手动埋点计时
location /api/product {
access_by_lua_block {
local start = ngx.now()
-- 业务逻辑代码
local res = query_database("SELECT * FROM products")
process_product_data(res)
local cost = (ngx.now() - start) * 1000 -- 转换为毫秒
ngx.log(ngx.INFO, "[PERF] product_api cost ", cost, "ms")
}
}
特点:适合快速验证,但需要侵入代码
3.2 增强版:共享字典统计
http {
lua_shared_dict perf_stats 10m; -- 声明共享内存区域
}
location = /report {
content_by_lua_block {
local stats = ngx.shared.perf_stats
local sum = stats:get("product_api_sum") or 0
local count = stats:get("product_api_count") or 0
ngx.say("平均耗时:", sum/count, "ms")
}
}
location /api {
access_by_lua_block {
local start = ngx.now()
-- 执行业务逻辑
handle_api_request()
local cost = (ngx.now() - start) * 1000
local stats = ngx.shared.perf_stats
stats:incr("product_api_sum", cost)
stats:incr("product_api_count", 1)
}
}
优势:数据聚合更便捷,适合长期监控
3.3 高阶版:请求全链路追踪
server {
log_by_lua_block {
local ctx = ngx.ctx
ngx.log(ngx.INFO,
"[TRACE] total=", ctx.total_cost or 0,
" db=", ctx.db_cost or 0,
" cache=", ctx.cache_cost or 0)
}
location /api {
access_by_lua_block {
ngx.ctx.total_start = ngx.now()
-- 数据库操作
local db_start = ngx.now()
query_database()
ngx.ctx.db_cost = (ngx.now() - db_start) * 1000
-- 缓存操作
local cache_start = ngx.now()
query_cache()
ngx.ctx.cache_cost = (ngx.now() - cache_start) * 1000
ngx.ctx.total_cost = (ngx.now() - ngx.ctx.total_start) * 1000
}
}
}
亮点:支持多阶段耗时分析,精准定位瓶颈
3.4 终极方案:动态采样监控
local function should_sample()
-- 按1%概率采样
return math.random(100) == 1
end
location /api {
access_by_lua_block {
local sample = should_sample()
local start = sample and ngx.now() or nil
process_request()
if sample then
local cost = (ngx.now() - start) * 1000
ngx.log(ngx.INFO, "[SAMPLING] cost=", cost)
end
}
}
适用场景:高并发下的性能监控,有效降低系统开销
四、技术方案对比分析
方案 | 精度 | 性能损耗 | 实施难度 | 数据维度 |
---|---|---|---|---|
手动埋点 | 高 | 中 | 低 | 单一 |
共享字典 | 中 | 低 | 中 | 聚合 |
全链路追踪 | 高 | 高 | 高 | 多维 |
动态采样 | 可变 | 最低 | 中 | 样本 |
五、五大核心注意事项
- 时间精度陷阱:
-- 错误示例(浮点数精度丢失)
local t1 = ngx.now()
local t2 = ngx.now()
assert(t2 > t1) -- 可能在极短时间内失败
-- 正确做法(使用整数存储)
local t1 = ngx.now() * 1000
local t2 = ngx.now() * 1000
- 日志级别风暴:
# nginx.conf 调整日志级别
error_log logs/error.log info; -- 生产环境建议warn
- 共享字典的原子操作:
local stats = ngx.shared.perf_stats
stats:incr("total_cost", cost) -- 原子操作安全
-- stats:set("total", stats:get("total") + cost) -- 非原子,存在竞态风险
- 第三方库的版本适配:
# 安装指定版本的lua-resty-core
opm install openresty/lua-resty-core@0.1.24
- 监控指标的维度设计:
-- 好的指标示例
local metric_name = "api_"..ngx.var.host.."_"..ngx.var.uri
-- 差的指标示例
local metric_name = "api_cost" -- 缺乏维度信息
六、典型应用场景解析
6.1 突发流量下的自动熔断
local circuit_breaker = {
last_failure_time = 0,
failure_count = 0
}
access_by_lua_block {
local start = ngx.now()
-- 业务逻辑
handle_request()
local cost = (ngx.now() - start) * 1000
if cost > 1000 then -- 超过1秒视为异常
circuit_breaker.failure_count = circuit_breaker.failure_count + 1
if circuit_breaker.failure_count > 10 then
ngx.exit(503) -- 触发熔断
end
end
}
6.2 灰度发布验证
location /api {
access_by_lua_block {
local start = ngx.now()
-- 新版本代码
new_feature()
local cost = (ngx.now() - start) * 1000
report_to_monitor("v2", cost) -- 单独统计新版本耗时
}
}
6.3 慢查询自动分析
local slow_log = ngx.config.subsystem == "http" and
require "ngx.errlog" .get_subsystem("http")
location /api {
content_by_lua_block {
local query_start = ngx.now()
run_sql_query()
local cost = (ngx.now() - query_start) * 1000
if cost > 500 then
ngx.log(ngx.WARN, "SLOW_QUERY:", ngx.var.request_uri)
slow_log.record("sql_slow", cost) -- 记录到专用日志
end
}
}
七、总结与展望
通过本文介绍的四种监控方案,我们可以根据实际场景灵活组合使用。未来趋势方面,OpenResty社区正在推动将更多的监控指标集成到ngx_metric模块中,预计2024年发布的OpenResty 2.0可能会内置Prometheus格式的指标导出功能。
建议在生产环境中采用分层监控策略:
- 全量采集基础耗时指标
- 按1%采样率收集详细跟踪数据
- 异常耗时自动触发线程快照
- 结合OpenTracing实现分布式追踪