子请求是nginx内部轻量级嵌套调用机制,全程内存中完成,不走网络;其为独立ngx_http_request_t结构体,复用主请求连接与内存池,需目标location设为internal且uri以/开头;c模块中通过ngx_http_subrequest发起,配合回调聚合结果,调试建议启用log_subrequest。

子请求不是转发,也不是代理,它是 Nginx 内部轻量级的嵌套调用机制——全程不走网络、不建连接、不解析 DNS,只在内存和事件循环中完成。
子请求的核心特征
它本质是一个独立的 ngx_http_request_t 结构体,但复用主请求的连接(r->connection)和内存池(需注意生命周期)。关键字段包括:parent 指向发起者、main 指向原始主请求、subrequest:1 标志位标识身份。子请求跳过请求读取阶段,直接进入处理流程,响应也不自动发给客户端,必须由主请求显式捕获或合并。
发起子请求的硬性前提
目标 location 必须声明为 internal,否则会被外部直接访问,带来安全与缓存风险。URI 必须以 / 开头,且不能是公开可路由的路径。例如:
location /_user { internal; proxy_pass http://user-svc; }location /_config { internal; return 200 '{ "env": "prod" }'; }
C 模块中调用 subrequest 的基本步骤
在自定义 C 模块里,需调用 ngx_http_subrequest 函数,传入四个核心参数:
- 当前主请求指针
r - 目标 URI(
ngx_str_t类型,如ngx_string("/_user")) - 可选查询参数(
args,如&uid=123) - 输出参数
psr,用于接收子请求对象指针
还需指定标志位,常用 NGX_HTTP_SUBREQUEST_IN_MEMORY 强制将响应体读入内存,避免流式处理引入延迟。
回调与聚合的关键逻辑
子请求完成后会触发回调函数,但不能在回调里直接操作主请求响应头或发送数据。正确做法是:
- 把聚合所需的状态(如待完成数、结果数组)挂载到主请求的
r->ctx或独立分配的上下文结构中 - 每次回调递减计数器,仅当计数归零时统一构造响应体并调用
ngx_http_send_header和ngx_http_output_filter - 避免在回调中调用
ngx_http_finalize_request终止主请求,否则未完成的子请求会被强制清理
调试与可观测性建议
启用 log_subrequest on 后,Nginx 会为每个子请求单独记录日志,变量(如 $uri、$status)取值来自子请求上下文,而非主请求。配合 $request_id 和自定义 log_format,可清晰追踪调用链与耗时。默认关闭该选项,正是为了避免日志语义混淆。











