- 新增 lua-embed-analysis.md 技术分析文档 - 新增 lua-nginx-module 文档目录 - 更新 gitignore 允许跟踪 docs/lua-nginx-module/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.2 KiB
4.2 KiB
lua-nginx-module 代码缓存与异常处理
本文档详细说明 lua-nginx-module 的代码缓存和异常处理机制。
一、代码缓存机制
1.1 核心文件
| 文件 | 说明 |
|---|---|
src/ngx_http_lua_cache.c |
代码缓存实现 |
src/ngx_http_lua_clfactory.c |
Closure factory 实现 |
src/ngx_http_lua_directive.c |
lua_code_cache 指令处理 |
1.2 lua_code_cache 指令
lua_code_cache on; # 默认,代码缓存
lua_code_cache off; # 开发模式,每次重载
影响:
- on: 每个 worker 共享一个 Lua VM,代码加载一次并缓存
- off: 每个请求创建独立 Lua VM,代码每次重载
1.3 缓存键生成
Inline Script:
- 格式:
nhli_{MD5(src)} - 宏:
NGX_HTTP_LUA_INLINE_TAG
u_char *ngx_http_lua_gen_chunk_cache_key(ngx_conf_t *cf,
const char *tag, const u_char *src, size_t src_len)
{
// key = tag + "_" + "nhli_" + MD5(src)
}
File Script:
- 格式:
nhlf_{MD5(file_path)} - 宏:
NGX_HTTP_LUA_FILE_TAG
1.4 Closure Factory 模式
非 LuaJIT 环境:
#define CLFACTORY_BEGIN_CODE "return function() "
#define CLFACTORY_END_CODE "\nend"
用户代码被包装为:
return function()
<user_code>
end
- 缓存: 工厂函数被缓存
- 执行: 每次调用工厂创建新 closure,隔离 upvalue
LuaJIT 环境:
- 直接缓存编译后的函数
- 不需要工厂包装
1.5 缓存查找/存储流程
查找:
static ngx_int_t ngx_http_lua_cache_load_code(...)
{
// 1. 获取 Registry 中的缓存表
lua_pushlightuserdata(L, code_cache_key);
lua_rawget(L, LUA_REGISTRYINDEX);
// 2. 查找缓存
lua_getfield(L, -1, key);
if (lua_isfunction(L, -1)) {
// 命中
#ifdef OPENRESTY_LUAJIT
return NGX_OK;
#else
// 调用工厂生成新 closure
lua_pcall(L, 0, 1, 0);
#endif
}
return NGX_DECLINED; // 未命中
}
存储:
static ngx_int_t ngx_http_lua_cache_store_code(...)
{
// 1. 获取缓存表
lua_pushlightuserdata(L, code_cache_key);
lua_rawget(L, LUA_REGISTRYINDEX);
// 2. 存储函数
lua_pushvalue(L, -2); // 复制 closure
lua_setfield(L, -2, key); // 存入缓存表
}
二、异常处理机制
2.1 核心文件
| 文件 | 说明 |
|---|---|
src/ngx_http_lua_exception.c |
异常处理实现 |
src/ngx_http_lua_exception.h |
宏定义 |
2.2 setjmp/longjmp 机制
#define NGX_LUA_EXCEPTION_TRY if (setjmp(ngx_http_lua_exception) == 0)
#define NGX_LUA_EXCEPTION_CATCH else
#define NGX_LUA_EXCEPTION_THROW(x) longjmp(ngx_http_lua_exception, (x))
2.3 Panic Handler
static int ngx_http_lua_atpanic(lua_State *L)
{
// 1. 输出错误日志
// 2. 通过 longjmp 恢复 nginx 执行
NGX_LUA_EXCEPTION_THROW(1);
}
2.4 使用模式
ngx_http_lua_exception_init();
NGX_LUA_EXCEPTION_TRY {
// 执行 Lua 代码
rc = lua_pcall(L, 0, 1, 0);
} NGX_LUA_EXCEPTION_CATCH {
// 捕获异常
ngx_log_error(NGX_LOG_ERR, log, 0, "Lua VM panic");
}
三、VM 管理
3.1 VM 初始化
ngx_int_t ngx_http_lua_init_vm(lua_State **L, ...)
{
// 1. 创建新 Lua 状态机
*L = luaL_newstate();
// 2. 加载标准库
luaL_openlibs(*L);
// 3. 注入 ngx.* API
ngx_http_lua_inject_ngx_api(*L, lmcf, log);
// 4. 设置 panic handler
lua_atpanic(*L, ngx_http_lua_atpanic);
}
3.2 VM 状态缓存
lua_code_cache off 模式下的 VM 状态缓存:
typedef struct {
lua_State *lua; // Lua VM
ngx_pool_t *pool;
// ...
} ngx_http_lua_vm_state_t;
四、最佳实践
4.1 开发环境
lua_code_cache off; # 代码实时生效
location /reload {
content_by_lua_block {
-- 无需重新加载配置
}
}
4.2 生产环境
lua_code_cache on;
# 使用信号重载代码
# nginx -s reload
4.3 错误处理
local ok, err = pcall(function()
-- 可能出错的代码
end)
if not ok then
ngx.log(ngx.ERR, "Error: ", err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end