- 新增 lua-embed-analysis.md 技术分析文档 - 新增 lua-nginx-module 文档目录 - 更新 gitignore 允许跟踪 docs/lua-nginx-module/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.4 KiB
4.4 KiB
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 |
四、选项参数
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 = {} -- 传递上下文
})
五、响应结构
local res = ngx.location.capture("/api")
-- res = {
-- status = 200, -- HTTP 状态码
-- header = {...}, -- 响应头 table
-- body = "...", -- 响应体
-- truncated = false -- 是否被截断
-- }
六、使用示例
6.1 基本用法
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 请求
local res = ngx.location.capture("/api/create", {
method = ngx.HTTP_POST,
body = '{"name":"john"}',
headers = {
["Content-Type"] = "application/json"
}
})
6.3 并发请求
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 数据存储结构
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 响应捕获过滤器
// 头部过滤器
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 响应组装
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 等方法 | 不转发原请求体 |
| 其他情况 | 深拷贝原请求体 |
九、注意事项
- 子请求必须是内部 location(以
/开头或使用internal指令) - 嵌套深度有限制(Nginx 默认 50)
- 不能跨 server 块
- 异步操作会挂起父请求