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

4.4 KiB
Raw Permalink Blame History

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 等方法 不转发原请求体
其他情况 深拷贝原请求体

九、注意事项

  1. 子请求必须是内部 location(以 / 开头或使用 internal 指令)
  2. 嵌套深度有限制Nginx 默认 50
  3. 不能跨 server 块
  4. 异步操作会挂起父请求