- 新增 lua-embed-analysis.md 技术分析文档 - 新增 lua-nginx-module 文档目录 - 更新 gitignore 允许跟踪 docs/lua-nginx-module/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
366 lines
8.2 KiB
Markdown
366 lines
8.2 KiB
Markdown
# lua-nginx-module 定时器和用户线程 API
|
|
|
|
本文档详细说明 lua-nginx-module 的定时器和用户线程功能。
|
|
|
|
---
|
|
|
|
## 一、Timer API
|
|
|
|
### 核心文件
|
|
|
|
- `src/ngx_http_lua_timer.c` - 定时器核心实现
|
|
- `src/ngx_http_lua_timer.h` - 定时器头文件
|
|
|
|
### 1.1 `ngx.timer.at(delay, callback, ...)`
|
|
|
|
创建一次性定时器。
|
|
|
|
- **参数**:
|
|
- `delay` (number): 延迟秒数,支持小数
|
|
- `callback` (function): 回调函数
|
|
- `...`: 传递给回调的额外参数
|
|
- **返回值**: timer_id (成功) 或 nil, err
|
|
- **回调参数**: `premature`, `...` (额外参数)
|
|
- **示例**:
|
|
```lua
|
|
local function handler(premature, data)
|
|
if premature then
|
|
return -- Worker 正在退出
|
|
end
|
|
ngx.log(ngx.INFO, "Timer fired: ", data)
|
|
end
|
|
|
|
local ok, err = ngx.timer.at(5, handler, "hello")
|
|
if not ok then
|
|
ngx.log(ngx.ERR, "failed to create timer: ", err)
|
|
end
|
|
```
|
|
|
|
### 1.2 `ngx.timer.every(delay, callback, ...)`
|
|
|
|
创建周期性定时器。
|
|
|
|
- **参数**: 同 `ngx.timer.at`
|
|
- **返回值**: timer_id 或 nil, err
|
|
- **示例**:
|
|
```lua
|
|
local function heartbeat()
|
|
-- 定期执行任务
|
|
local redis = require "resty.redis"
|
|
-- ...
|
|
end
|
|
|
|
ngx.timer.every(30, heartbeat) -- 每 30 秒执行
|
|
```
|
|
|
|
### 1.3 `ngx.timer.running_count()`
|
|
|
|
获取当前正在执行的定时器数量。
|
|
|
|
- **返回值**: number
|
|
|
|
### 1.4 `ngx.timer.pending_count()`
|
|
|
|
获取等待执行的定时器数量。
|
|
|
|
- **返回值**: number
|
|
|
|
### 1.5 定时器限制
|
|
|
|
| 配置指令 | 默认值 | 说明 |
|
|
|---------|--------|------|
|
|
| `lua_max_pending_timers` | 1024 | 最大挂起定时器数 |
|
|
| `lua_max_running_timers` | 256 | 最大运行定时器数 |
|
|
|
|
### 1.6 内部实现
|
|
|
|
**数据结构**:
|
|
|
|
```c
|
|
typedef struct {
|
|
void **main_conf;
|
|
void **srv_conf;
|
|
void **loc_conf;
|
|
lua_State *co; // 定时器回调协程
|
|
ngx_pool_t *pool;
|
|
int co_ref;
|
|
unsigned delay:31; // 周期性定时器的延迟
|
|
unsigned premature:1; // 是否被提前终止
|
|
} ngx_http_lua_timer_ctx_t;
|
|
```
|
|
|
|
**执行机制**:
|
|
|
|
1. 创建 `ngx_event_t` + `ngx_http_lua_timer_ctx_t`
|
|
2. 使用 `ngx_add_timer(ev, delay)` 加入 Nginx 红黑树定时器
|
|
3. 定时器到期时,`ngx_http_lua_timer_handler` 执行
|
|
4. 创建 fake connection/request 模拟请求上下文
|
|
|
|
---
|
|
|
|
## 二、Coroutine API
|
|
|
|
### 核心文件
|
|
|
|
- `src/ngx_http_lua_coroutine.c` - 协程核心实现
|
|
- `src/ngx_http_lua_coroutine.h` - 协程头文件
|
|
|
|
### 2.1 协程状态
|
|
|
|
```c
|
|
typedef enum {
|
|
NGX_HTTP_LUA_CO_RUNNING = 0, // 运行中
|
|
NGX_HTTP_LUA_CO_SUSPENDED = 1, // 挂起
|
|
NGX_HTTP_LUA_CO_NORMAL = 2, // 正常(被 resume 的协程)
|
|
NGX_HTTP_LUA_CO_DEAD = 3, // 已结束
|
|
NGX_HTTP_LUA_CO_ZOMBIE = 4 // 僵尸(父协程已结束)
|
|
} ngx_http_lua_co_status_e;
|
|
```
|
|
|
|
### 2.2 标准 Coroutine API
|
|
|
|
lua-nginx-module 扩展了 Lua 标准 coroutine 库:
|
|
|
|
| API | 说明 |
|
|
|-----|------|
|
|
| `coroutine.create(func)` | 创建新协程 |
|
|
| `coroutine.wrap(func)` | 创建包装协程 |
|
|
| `coroutine.resume(co, ...)` | 恢复协程执行 |
|
|
| `coroutine.yield(...)` | 挂起当前协程 |
|
|
| `coroutine.status(co)` | 获取协程状态 |
|
|
|
|
### 2.3 设计特点
|
|
|
|
- 所有用户协程都在主协程中创建
|
|
- 确保总是 yield 到主 Lua 线程
|
|
- 使用 `ngx_http_lua_co_ctx_t` 管理协程上下文
|
|
|
|
---
|
|
|
|
## 三、User Thread (Light Thread) API
|
|
|
|
### 核心文件
|
|
|
|
- `src/ngx_http_lua_uthread.c` - 用户线程实现
|
|
- `src/ngx_http_lua_uthread.h` - 用户线程头文件
|
|
|
|
### 3.1 概念说明
|
|
|
|
Light Thread (uthread) 是一种特殊的协程:
|
|
- 通过 `ngx.thread.*` API 操作
|
|
- 父子关系严格:只有父协程可以 wait/kill 子线程
|
|
- 使用 `is_uthread` 标记区分
|
|
|
|
### 3.2 API
|
|
|
|
#### `ngx.thread.spawn(func, ...)`
|
|
|
|
创建轻量级线程。
|
|
|
|
- **参数**:
|
|
- `func` (function): 线程函数
|
|
- `...`: 传递给函数的参数
|
|
- **返回值**: thread 对象
|
|
- **示例**:
|
|
```lua
|
|
local function fetch(url)
|
|
local sock = ngx.socket.tcp()
|
|
-- ... 网络操作
|
|
return result
|
|
end
|
|
|
|
local thread1 = ngx.thread.spawn(fetch, "http://api1")
|
|
local thread2 = ngx.thread.spawn(fetch, "http://api2")
|
|
```
|
|
|
|
#### `ngx.thread.wait(thread1, thread2, ...)`
|
|
|
|
等待线程结束。
|
|
|
|
- **参数**: 一个或多个 thread 对象
|
|
- **返回值**: success, res_or_err, thread
|
|
- **示例**:
|
|
```lua
|
|
local ok, res = ngx.thread.wait(thread1, thread2)
|
|
if ok then
|
|
ngx.say("Result: ", res)
|
|
end
|
|
```
|
|
|
|
#### `ngx.thread.kill(thread)`
|
|
|
|
终止线程。
|
|
|
|
- **参数**: thread 对象
|
|
- **返回值**: 1 或 nil, err
|
|
|
|
### 3.3 判断宏
|
|
|
|
```c
|
|
#define ngx_http_lua_is_thread(ctx) \
|
|
((ctx)->cur_co_ctx->is_uthread || (ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)
|
|
|
|
#define ngx_http_lua_is_entry_thread(ctx) \
|
|
((ctx)->cur_co_ctx == &(ctx)->entry_co_ctx)
|
|
|
|
#define ngx_http_lua_coroutine_alive(coctx) \
|
|
((coctx)->co_status != NGX_HTTP_LUA_CO_DEAD \
|
|
&& (coctx)->co_status != NGX_HTTP_LUA_CO_ZOMBIE)
|
|
```
|
|
|
|
---
|
|
|
|
## 四、Semaphore API
|
|
|
|
### 核心文件
|
|
|
|
- `src/ngx_http_lua_semaphore.c` - 信号量实现
|
|
- `src/ngx_http_lua_semaphore.h` - 信号量头文件
|
|
|
|
### 4.1 数据结构
|
|
|
|
```c
|
|
typedef struct ngx_http_lua_sema_s {
|
|
ngx_queue_t wait_queue; // 等待队列
|
|
int resource_count; // 资源计数
|
|
unsigned wait_count; // 等待计数
|
|
} ngx_http_lua_sema_t;
|
|
```
|
|
|
|
### 4.2 API
|
|
|
|
#### `ngx.semaphore.new(n)`
|
|
|
|
创建信号量。
|
|
|
|
- **参数**: `n` (number) - 初始资源数
|
|
- **返回值**: semaphore 对象 或 nil, err
|
|
- **示例**:
|
|
```lua
|
|
local semaphore = require "ngx.semaphore"
|
|
local sem, err = semaphore.new(0)
|
|
```
|
|
|
|
#### `sem:post(n?)`
|
|
|
|
释放资源。
|
|
|
|
- **参数**: `n` (number, 可选, 默认 1) - 释放数量
|
|
- **示例**:
|
|
```lua
|
|
sem:post(1) -- 释放 1 个资源
|
|
```
|
|
|
|
#### `sem:wait(timeout)`
|
|
|
|
等待资源。
|
|
|
|
- **参数**: `timeout` (number) - 超时秒数
|
|
- **返回值**: 1 或 nil, err
|
|
- **示例**:
|
|
```lua
|
|
local ok, err = sem:wait(5) -- 最多等待 5 秒
|
|
if not ok then
|
|
ngx.log(ngx.ERR, "wait timeout: ", err)
|
|
end
|
|
```
|
|
|
|
#### `sem:count()`
|
|
|
|
获取可用资源数。
|
|
|
|
- **返回值**: number
|
|
|
|
### 4.3 使用示例
|
|
|
|
```lua
|
|
local semaphore = require "ngx.semaphore"
|
|
|
|
local sem = semaphore.new(0)
|
|
|
|
-- 生产者
|
|
local function producer()
|
|
for i = 1, 10 do
|
|
ngx.sleep(0.1)
|
|
sem:post(1)
|
|
ngx.log(ngx.INFO, "produced: ", i)
|
|
end
|
|
end
|
|
|
|
-- 消费者
|
|
local function consumer()
|
|
for i = 1, 10 do
|
|
sem:wait(1)
|
|
ngx.log(ngx.INFO, "consumed: ", i)
|
|
end
|
|
end
|
|
|
|
ngx.thread.spawn(producer)
|
|
ngx.thread.spawn(consumer)
|
|
```
|
|
|
|
---
|
|
|
|
## 五、关系图
|
|
|
|
```
|
|
ngx.timer.* ─────────────────────────────┐
|
|
│ │
|
|
▼ │
|
|
ngx_event_t (Nginx 定时器红黑树) │
|
|
│ │
|
|
▼ │
|
|
lua_State (协程) ◄── coroutine.create │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ngx_http_lua_co_ctx_t │
|
|
│ │ │
|
|
└─────────► ngx.thread.spawn ─────────►│
|
|
(uthread/light thread) │
|
|
│ │
|
|
▼ │
|
|
ngx.semaphore.new │
|
|
│ │
|
|
▼ │
|
|
ngx_http_lua_sema_t ───────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 六、最佳实践
|
|
|
|
### 6.1 定时器清理
|
|
|
|
```lua
|
|
local timer_id
|
|
|
|
local function cleanup()
|
|
-- Worker 退出时清理
|
|
ngx.log(ngx.INFO, "cleaning up...")
|
|
end
|
|
|
|
timer_id = ngx.timer.every(60, function(premature)
|
|
if premature then
|
|
cleanup()
|
|
return
|
|
end
|
|
-- 正常任务
|
|
end)
|
|
```
|
|
|
|
### 6.2 并发控制
|
|
|
|
```lua
|
|
local semaphore = require "ngx.semaphore"
|
|
local sem = semaphore.new(3) -- 最大 3 个并发
|
|
|
|
local function limited_task()
|
|
sem:wait(10) -- 等待获取资源
|
|
-- 执行任务
|
|
sem:post(1) -- 释放资源
|
|
end
|
|
|
|
for i = 1, 10 do
|
|
ngx.thread.spawn(limited_task)
|
|
end
|
|
``` |