lolly/docs/lua-nginx-module/07-subrequest.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

200 lines
4.4 KiB
Markdown
Raw Permalink 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 Subrequest 内部请求
本文档详细说明 lua-nginx-module 的子请求功能。
---
## 一、核心文件
| 文件 | 说明 |
|------|------|
| `src/ngx_http_lua_subrequest.c` | 子请求实现 |
| `src/ngx_http_lua_capturefilter.c` | 响应捕获过滤器 |
---
## 二、API 概述
### `ngx.location.capture(uri, options?)`
发起单个子请求。
### `ngx.location.capture_multi(requests)`
并发发起多个子请求。
**关键发现**: `capture` 实际上是 `capture_multi` 的薄包装。
---
## 三、支持的 HTTP 方法
| 常量 | 方法 |
|------|------|
| `ngx.HTTP_GET` | GET |
| `ngx.HTTP_POST` | POST |
| `ngx.HTTP_PUT` | PUT |
| `ngx.HTTP_DELETE` | DELETE |
| `ngx.HTTP_HEAD` | HEAD |
| `ngx.HTTP_PATCH` | PATCH |
| `ngx.HTTP_OPTIONS` | OPTIONS |
---
## 四、选项参数
```lua
local res = ngx.location.capture(uri, {
method = ngx.HTTP_POST, -- HTTP 方法
args = "foo=bar", -- 参数字符串或 table
body = '{"data":1}', -- 请求体
headers = { -- 请求头
["Content-Type"] = "application/json"
},
vars = { -- 变量
upstream = "backend"
},
share_all_vars = false, -- 共享所有变量
copy_all_vars = false, -- 复制所有变量
always_forward_body = false, -- 始终转发请求体
ctx = {} -- 传递上下文
})
```
---
## 五、响应结构
```lua
local res = ngx.location.capture("/api")
-- res = {
-- status = 200, -- HTTP 状态码
-- header = {...}, -- 响应头 table
-- body = "...", -- 响应体
-- truncated = false -- 是否被截断
-- }
```
---
## 六、使用示例
### 6.1 基本用法
```lua
local res = ngx.location.capture("/internal/users")
if res.status == 200 then
local users = cjson.decode(res.body)
ngx.say("Users: ", #users)
end
```
### 6.2 POST 请求
```lua
local res = ngx.location.capture("/api/create", {
method = ngx.HTTP_POST,
body = '{"name":"john"}',
headers = {
["Content-Type"] = "application/json"
}
})
```
### 6.3 并发请求
```lua
local res1, res2, res3 = ngx.location.capture_multi({
{"/api/users"},
{"/api/products", {method = ngx.HTTP_GET}},
{"/api/orders", {args = "status=pending"}}
})
ngx.say("Users: ", res1.status)
ngx.say("Products: ", res2.status)
ngx.say("Orders: ", res3.status)
```
---
## 七、内部实现
### 7.1 数据存储结构
```c
struct ngx_http_lua_co_ctx_s {
ngx_int_t *sr_statuses; // 子请求状态码数组
ngx_http_headers_out_t **sr_headers; // 子请求响应头数组
ngx_str_t *sr_bodies; // 子请求响应体数组
uint8_t *sr_flags; // 子请求标志位数组
unsigned nsubreqs; // 子请求总数
unsigned pending_subreqs; // 待处理子请求数
};
```
### 7.2 响应捕获过滤器
```c
// 头部过滤器
static ngx_int_t ngx_http_lua_capture_header_filter(ngx_http_request_t *r)
{
if (ctx && ctx->capture) {
r->filter_need_in_memory = 1; // 强制内存缓冲
return NGX_OK; // 拦截,不发送到客户端
}
}
// 响应体过滤器
static ngx_int_t ngx_http_lua_capture_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
// 将响应体复制到 ctx->body 链表
}
```
### 7.3 响应组装
```c
static void ngx_http_lua_handle_subreq_responses(...)
{
for (index = 0; index < coctx->nsubreqs; index++) {
lua_createtable(co, 0, 4); // 创建响应表
// status
lua_pushinteger(co, coctx->sr_statuses[index]);
lua_setfield(co, -2, "status");
// truncated
if (coctx->sr_flags[index] & NGX_HTTP_LUA_SUBREQ_TRUNCATED) {
lua_pushboolean(co, 1);
lua_setfield(co, -2, "truncated");
}
// body
lua_pushlstring(co, body_str->data, body_str->len);
lua_setfield(co, -2, "body");
// header
// ...
}
}
```
---
## 八、请求体传递规则
| 条件 | 行为 |
|------|------|
| 指定了 `body` 选项 | 使用自定义请求体 |
| GET/DELETE 等方法 | 不转发原请求体 |
| 其他情况 | 深拷贝原请求体 |
---
## 九、注意事项
1. **子请求必须是内部 location**(以 `/` 开头或使用 `internal` 指令)
2. **嵌套深度有限制**Nginx 默认 50
3. **不能跨 server 块**
4. **异步操作会挂起父请求**