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

228 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 指令
```nginx
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`
```c
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 环境**:
```c
#define CLFACTORY_BEGIN_CODE "return function() "
#define CLFACTORY_END_CODE "\nend"
```
用户代码被包装为:
```lua
return function()
<user_code>
end
```
- **缓存**: 工厂函数被缓存
- **执行**: 每次调用工厂创建新 closure隔离 upvalue
**LuaJIT 环境**:
- 直接缓存编译后的函数
- 不需要工厂包装
### 1.5 缓存查找/存储流程
**查找**:
```c
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; // 未命中
}
```
**存储**:
```c
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 机制
```c
#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
```c
static int ngx_http_lua_atpanic(lua_State *L)
{
// 1. 输出错误日志
// 2. 通过 longjmp 恢复 nginx 执行
NGX_LUA_EXCEPTION_THROW(1);
}
```
### 2.4 使用模式
```c
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 初始化
```c
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 状态缓存:
```c
typedef struct {
lua_State *lua; // Lua VM
ngx_pool_t *pool;
// ...
} ngx_http_lua_vm_state_t;
```
---
## 四、最佳实践
### 4.1 开发环境
```nginx
lua_code_cache off; # 代码实时生效
location /reload {
content_by_lua_block {
-- 无需重新加载配置
}
}
```
### 4.2 生产环境
```nginx
lua_code_cache on;
# 使用信号重载代码
# nginx -s reload
```
### 4.3 错误处理
```lua
local ok, err = pcall(function()
-- 可能出错的代码
end)
if not ok then
ngx.log(ngx.ERR, "Error: ", err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
```