lolly/docs/lua-nginx-module/10-code-cache.md
xfy 941c44b798 docs: 添加 Lua 嵌入分析文档
- 新增 lua-embed-analysis.md 技术分析文档
- 新增 lua-nginx-module 文档目录
- 更新 gitignore 允许跟踪 docs/lua-nginx-module/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 11:20:57 +08:00

4.2 KiB
Raw Blame History

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