AIGC标识 nginx-源码带读-03-HTTP处理的11个阶段

NGINX 源码带读 第3篇:HTTP处理的11个阶段

本篇目标

理解HTTP请求的完整生命周期,深入分析11个阶段处理流程、phase engine执行机制和checker函数工作原理。

前置知识

  • HTTP协议(请求行、请求头、请求体)
  • 阅读过第1、2篇

1. 连接到请求的完整流程

TCP连接到达
  -> ngx_event_accept()  接受连接
    -> ngx_http_init_connection()  初始化HTTP连接
      -> ngx_http_wait_request_handler()  等待请求数据
        -> ngx_http_init_request()  初始化请求
          -> ngx_http_create_request()  创建ngx_http_request_t
            -> ngx_http_alloc_request()  分配内存
        -> ngx_http_process_request_line()  解析请求行
          -> 状态机:method URI HTTP/1.x
        -> ngx_http_process_request_headers()  解析请求头
          -> 状态机:Key: Value\r\n
        -> ngx_http_process_request()  开始处理
          -> ngx_http_handler(r)  执行11个阶段
            -> ngx_http_core_run_phases()  阶段引擎
              -> ngx_http_finalize_request()  完成请求

1.1 请求解析状态机

NGINX使用有限状态机解析HTTP请求:

// src/http/ngx_http_request.c
// 请求行解析状态
typedef enum {
    sw_start = 0,
    sw_method,
    sw_space_before_uri,
    sw_schema,
    sw_schema_slash,
    sw_host_start,
    sw_host,
    sw_host_end,
    sw_host_http,
    sw_http_version,
    sw_http_09,
    sw_space_after_uri,
    sw_query_string_start,
    sw_query_string,
    sw_request_line_end,
} ngx_http_request_state_e;

// 解析流程
void ngx_http_process_request_line(ngx_http_request_t *r) {
    for ( ;; ) {
        if (r->header_in->pos == r->header_in->last) {
            // 需要更多数据
            ngx_http_read_request_header(r);
            return;
        }

        // 状态机驱动
        rc = ngx_http_parse_request_line(r, r->header_in);

        if (rc == NGX_OK) {
            // 解析完成,解析请求头
            ngx_http_process_request_headers(r);
        } else if (rc == NGX_AGAIN) {
            // 需要更多数据
            return;
        } else {
            // 解析错误
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        }
    }
}

2. ngx_http_request_t

每个HTTP请求对应一个 ngx_http_request_t 结构体(约4235行):

struct ngx_http_request_s {
    ngx_uint_t signature;            // NGX_HTTP_MODULE

    ngx_connection_t *connection;    // 底层连接
    ngx_http_connection_t *http_connection; // HTTP连接上下文

    ngx_pool_t *pool;                // 请求级内存池
    size_t header_pool_size;         // 头部内存池大小

    // 请求信息
    ngx_str_t request_line;          // "GET /path HTTP/1.1"
    ngx_str_t uri;                   // URI路径(未解码)
    ngx_str_t args;                  // 查询参数
    ngx_str_t exten;                 // 文件扩展名
    ngx_str_t method_name;           // "GET"
    ngx_str_t http_protocol;         // "HTTP/1.1"
    ngx_uint_t method;               // 方法枚举(NGX_HTTP_GET等)
    ngx_uint_t http_version;         // 版本号(1001 = 1.1)

    // 头部
    ngx_http_headers_in_t headers_in;    // 输入头部
    ngx_http_headers_out_t headers_out;  // 输出头部
    ngx_http_chunked_t chunked;          // chunk编码状态

    // 请求体
    ngx_http_request_body_t *request_body; // 请求体

    // 阶段处理
    ngx_uint_t phase_handler;        // 当前阶段处理器索引
    ngx_http_handler_t content_handler; // 内容处理函数
    ngx_http_handler_t write_event_handler; // 写事件处理函数

    // 配置指针(在FIND_CONFIG阶段设置)
    void **main_conf;                // main级配置
    void **srv_conf;                 // srv级配置
    void **loc_conf;                 // loc级配置

    // 上下文
    void **ctx;                      // 各模块的请求上下文

    // 子请求
    ngx_uint_t subrequests;          // 当前子请求数量(最大50)
    ngx_http_request_t *main;        // 主请求
    ngx_http_request_t *parent;      // 父请求

    // 引用计数
    ngx_int_t count;                 // 引用计数(子请求等)
    ngx_http_postponed_request_t *postponed; // 延后处理的子请求

    // 状态标志
    unsigned header_only:1;          // 只需要响应头
    unsigned keepalive:1;            // 保持连接
    unsigned discard_body:1;         // 丢弃请求体
    unsigned internal:1;             // 内部请求
    unsigned error_page:1;           // 错误页面

    // 临时变量
    ngx_int_t uri_changes;           // URI变更次数(最多10次)
    ngx_int_t limit_req_status;      // 限流状态码

    // 时间戳
    time_t start_sec;                // 请求开始时间(秒)
    ngx_msec_t start_msec;           // 请求开始时间(毫秒)
};

2.1 请求分配

// src/http/ngx_http_request.c
static ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c) {
    ngx_http_request_t *r;
    ngx_http_connection_t *hc;
    ngx_pool_t *pool;

    // 获取server配置中的请求池大小
    hc = c->data;
    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx[ngx_http_module],
                                         ngx_http_core_module);
    pool_size = cscf->request_pool_size;  // 默认4096

    // 创建请求级内存池
    pool = ngx_create_pool(pool_size, c->log);

    // 分配请求结构体
    r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
    r->pool = pool;
    r->connection = c;

    // 初始化关键字段
    r->signature = NGX_HTTP_MODULE;
    r->ctx = ngx_pcalloc(pool, sizeof(void *) * ngx_http_max_module);

    // 复制配置指针(快速路径,避免每次查找)
    r->main_conf = hc->conf_ctx[ngx_http_module]->main_conf;
    r->srv_conf = hc->conf_ctx[ngx_http_module]->srv_conf;
    r->loc_conf = hc->conf_ctx[ngx_http_module]->loc_conf;

    // 初始化默认值
    r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;  // 10+1
    r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;   // 50+1
    r->count = 1;

    // 设置时间戳(使用缓存时间,避免系统调用)
    tp = ngx_timeofday();
    r->start_sec = tp->sec;
    r->start_msec = tp->msec;

    // 初始化读事件处理
    r->read_event_handler = ngx_http_block_reading;

    return r;
}

2.2 引用计数

NGINX使用引用计数管理请求生命周期:

// 主请求 count = 1
// 子请求 count = 主请求.count + 1
// 读取请求体 count++

void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) {
    // 如果有子请求在进行
    if (r != r->main) {
        r->main->count--;
        if (r->main->count == 0) {
            // 所有子请求完成,释放主请求
            ngx_http_close_request(r->main);
        }
        return;
    }
    // ...
}

3. 11个处理阶段

3.1 阶段概览

// src/http/ngx_http_core_module.h
typedef enum {
    NGX_HTTP_POST_READ_PHASE = 0,      // 读完请求头后
    NGX_HTTP_SERVER_REWRITE_PHASE,     // 服务器级别重写
    NGX_HTTP_FIND_CONFIG_PHASE,        // 查找location配置(内部)
    NGX_HTTP_REWRITE_PHASE,            // location级别重写
    NGX_HTTP_POST_REWRITE_PHASE,       // 重写后处理
    NGX_HTTP_PREACCESS_PHASE,          // 访问控制前
    NGX_HTTP_ACCESS_PHASE,             // 访问控制
    NGX_HTTP_POST_ACCESS_PHASE,        // 访问控制后
    NGX_HTTP_PRECONTENT_PHASE,         // 内容处理前
    NGX_HTTP_CONTENT_PHASE,            // 内容处理
    NGX_HTTP_LOG_PHASE                 // 日志记录
} ngx_http_phases_e;

3.2 阶段handler数组

每个阶段有独立的handler数组,通过配置积累:

typedef struct {
    ngx_array_t handlers;  // 该阶段的handler数组
} ngx_http_phase_t;

// ngx_http_conf_ctx_t中:
typedef struct {
    ngx_array_t *handlers[NGX_HTTP_LOG_PHASE + 1];  // 11个阶段
} ngx_http_phase_engine_t;

3.3 各阶段详解

阶段0 POST_READ:读完所有请求头后执行

  • realip模块在此阶段工作(获取真实客户端IP)
  • 检查请求体大小限制

阶段1 SERVER_REWRITE:服务器级别的重写

  • server块中的rewrite指令在此阶段执行
  • 在location匹配之前执行

阶段2 FIND_CONFIG:查找location配置(NGINX内部)

  • 根据URI匹配location块
  • 用户不可配置此阶段
  • 匹配结果决定后续阶段使用哪个loc_conf

阶段3 REWRITE:location级别的重写

  • location块中的rewrite指令在此阶段执行
  • 可以返回重定向(301/302/307/308)
  • 可以设置内部跳转(break/last)

阶段4 POST_REWRITE:重写后的处理

  • 检查重写是否导致内部跳转
  • 如果URI改变,跳回FIND_CONFIG阶段重新匹配
  • 检查重写次数限制(最多10次)

阶段5 PREACCESS:访问控制前

  • limit_req(限流)和limit_conn(限连接数)在此阶段
  • limit_req检查请求速率
  • limit_conn检查并发连接数

阶段6 ACCESS:访问控制

  • allow/deny指令
  • auth_basic(HTTP基本认证)在此阶段
  • satisfy any/all逻辑

阶段7 POST_ACCESS:访问控制后

  • 如果被拒绝,在此阶段返回403
  • 处理satisfy any/all的结果

阶段8 PRECONTENT:内容处理前

  • mirror模块在此阶段(镜像流量)
  • try_files指令在此阶段(文件存在性检查)
  • 内部跳转

阶段9 CONTENT:内容处理

  • 这是最重要的阶段!
  • proxy_pass、fastcgi_pass、static文件服务在此阶段
  • 每个location只能有一个content handler

阶段10 LOG_PHASE:日志记录

  • access_log在此阶段写入
  • 请求结束时执行(在free_request中调用)

3.4 阶段处理器返回值

返回值 含义 后续动作
NGX_OK 继续下一阶段 checker推进phase_handler
NGX_DECLINED 跳过当前阶段的后续处理器 继续下一个handler
NGX_AGAIN 等待更多数据 暂停阶段引擎
NGX_DONE 请求已完成 停止阶段引擎
NGX_ERROR 发生错误 终结请求
NGX_HTTP_OK (200) 请求成功 终结请求
NGX_HTTP_NOT_FOUND (404) 未找到 返回404响应

3.5 阶段执行流程图

POST_READ --> SERVER_REWRITE --> FIND_CONFIG --> REWRITE --> POST_REWRITE
    |              |                  |             |             |
    v              v                  v             v             v
realip         rewrite           find_config    rewrite       post_rewrite
handler        handler           handler        handler       handler
    |              |                  |             |             |
    +--------------+------------------+-------------+-------------+
                                    |
                                    v
PREACCESS --> ACCESS --> POST_ACCESS --> PRECONTENT --> CONTENT --> LOG
    |            |           |              |             |          |
    v            v           v              v             v          v
limit_req    allow/deny   post_access    try_files    proxy_pass  access_log
limit_conn   auth_basic   handler        mirror       fastcgi     write_log

4. ngx_http_core_run_phases —— 阶段引擎

// src/http/ngx_http_core_module.c
void ngx_http_core_run_phases(ngx_http_request_t *r) {
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_phase_engine_t    *engine;

    engine = r->phase_engine;

    // 从当前phase_handler开始执行
    ph = engine->handlers;
    rc = NGX_OK;

    // 循环执行checker,直到checker返回NGX_OK或没有checker
    while (ph[r->phase_handler].checker) {
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
        if (rc == NGX_OK) {
            break;  // 请求已完成或暂停
        }
    }
}

4.1 phase_handler结构

typedef struct ngx_http_phase_handler_s {
    ngx_http_phase_handler_pt  checker;  // 检查器函数
    ngx_http_handler_pt        handler;  // 实际处理函数
    ngx_uint_t                 next;     // 下一个phase_handler索引
} ngx_http_phase_handler_t;

4.2 checker函数

每个阶段有专门的checker函数,决定如何推进phase_engine:

通用checker(用于POST_READ、PREACCESS等)

static ngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph) {
    ngx_int_t  rc;

    // 调用handler
    rc = ph->handler(r);

    if (rc == NGX_OK) {
        // 继续下一个phase_handler
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    if (rc == NGX_DECLINED) {
        // 继续当前阶段的下一个handler
        r->phase_handler++;
        return NGX_AGAIN;
    }

    if (rc == NGX_AGAIN || rc == NGX_DONE) {
        // 暂停阶段引擎
        return NGX_OK;
    }

    // 其他返回值:终结请求
    r->phase_handler = ph->next;
    return NGX_AGAIN;
}

rewrite阶段checker

static ngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph) {
    ngx_int_t  rc;

    rc = ph->handler(r);

    if (rc == NGX_DECLINED) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    if (rc == NGX_DONE || rc == NGX_OK || rc == NGX_HTTP_SET_URI) {
        // 重写完成,跳到FIND_CONFIG重新匹配
        if (ph->next) {
            r->phase_handler = ph->next;
            return NGX_AGAIN;
        }
        return NGX_OK;
    }

    // 其他返回值:终结请求
    ngx_http_finalize_request(r, rc);
    return NGX_OK;
}

access阶段checker

static ngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph) {
    ngx_int_t  rc;

    // 子请求跳过access阶段
    if (r != r->main) {
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    // 检查是否有content_handler(内部跳转)
    if (r->content_handler) {
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    rc = ph->handler(r);

    if (rc == NGX_DECLINED) {
        r->phase_handler++;
        return NGX_AGAIN;
    }

    if (rc == NGX_AGAIN || rc == NGX_DONE) {
        return NGX_OK;
    }

    // 访问控制结果
    if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {
        // 设置access_code,在POST_ACCESS阶段处理
        r->access_code = rc;
        r->phase_handler = ph->next;
        return NGX_AGAIN;
    }

    // 其他返回值
    r->phase_handler = ph->next;
    return NGX_AGAIN;
}

content阶段checker

static ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph) {
    // 如果有content_handler(proxy_pass等设置的)
    if (r->content_handler) {
        // 直接调用content_handler
        r->content_handler(r);
        return NGX_OK;
    }

    // 否则使用阶段handler
    rc = ph->handler(r);

    if (rc == NGX_DECLINED) {
        // 继续下一个content handler
        r->phase_handler++;
        return NGX_AGAIN;
    }

    // 其他返回值:终结请求
    ngx_http_finalize_request(r, rc);
    return NGX_OK;
}

4.3 阶段引擎执行流程

ngx_http_core_run_phases()
  |
  +-> while (ph[phase_handler].checker)
        |
        +-> checker(r, ph) --+
          ^                    |
          |   NGX_AGAIN <------+
          |   (继续循环)
          |
          +-> NGX_OK (停止)
              |
              +-> 请求完成或暂停

5. 阶段编译

5.1 ngx_http_init_phase_handlers()

static ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf,
    ngx_http_phase_engine_t *engine) {
    ngx_http_phase_t        *ph;
    ngx_http_phase_handler_t *handlers;
    ngx_http_phase_handler_t *last;
    ngx_http_handler_pt     *log;

    // 计算总handler数
    n = 1;  // find_config阶段必须有handler
    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        n += ph[i].handlers.nelts;
    }

    // 分配handler数组
    handlers = ngx_pcalloc(cf->pool, n * sizeof(ngx_http_phase_handler_t));

    // 填充handler
    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {
        if (ph[i].handlers.nelts == 0) {
            continue;
        }

        // 逆序填充(后添加的handler先执行)
        for (j = ph[i].handlers.nelts - 1; j >= 0; j--) {
            handler = ph[i].handlers.elts;
            handlers->handler = handler[j];

            // 设置checker
            switch (i) {
            case NGX_HTTP_POST_READ_PHASE:
                handlers->checker = ngx_http_core_generic_phase;
                break;
            case NGX_HTTP_SERVER_REWRITE_PHASE:
                handlers->checker = ngx_http_core_rewrite_phase;
                break;
            case NGX_HTTP_FIND_CONFIG_PHASE:
                handlers->checker = ngx_http_core_find_config_phase;
                break;
            // ... 其他阶段
            }

            // 设置next指针
            if (i < NGX_HTTP_FIND_CONFIG_PHASE) {
                handlers->next = i + 1;
            } else if (i == NGX_HTTP_FIND_CONFIG_PHASE) {
                handlers->next = i + 2;  // 跳过find_config
            } else {
                handlers->next = i + 1;
            }

            handlers++;
        }
    }

    // 设置阶段引擎
    engine->handlers = handlers;
}

6. 一个典型的请求处理示例

server {
    listen 80;
    server_name example.com;

    location /api {
        rewrite ^/api/(.*)$ /$1 break;  # REWRITE阶段
        proxy_pass http://backend;       # CONTENT阶段
    }

    location /static {
        root /var/www;                   # CONTENT阶段(静态文件)
    }
}

请求 GET /api/users 的处理:

POST_READ
  -> realip模块获取真实IP
  -> 检查请求头完整性

SERVER_REWRITE
  -> 无server级rewrite

FIND_CONFIG
  -> 匹配location /api
  -> 设置r->loc_conf = /api的配置

REWRITE
  -> rewrite ^/api/(.*)$ /$1 break
  -> URI从 /api/users 变为 /users
  -> break标志:不继续重写

POST_REWRITE
  -> URI已改变,跳回FIND_CONFIG重新匹配

FIND_CONFIG(第二次)
  -> 匹配location /api(重写后的URI仍匹配)

PREACCESS
  -> limit_req检查
  -> limit_conn检查

ACCESS
  -> 检查allow/deny
  -> 检查auth_basic

POST_ACCESS
  -> 无(访问控制通过)

PRECONTENT
  -> try_files检查

CONTENT
  -> proxy_pass http://backend
  -> 代理到后端服务器
  -> 接收响应
  -> 发送响应

LOG
  -> 写入access_log

7. 子请求(Subrequest)

NGINX支持将一个请求分解为多个子请求:

主请求 GET /page
  -> 子请求1 GET /header  (SSI include)
  -> 子请求2 GET /content
  -> 子请求3 GET /footer

7.1 子请求创建

// src/http/ngx_http_request.c
ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
    ngx_str_t *uri, ngx_str_t *args,
    ngx_http_request_t **psr,
    ngx_http_post_subrequest_t *handler, ngx_uint_t flags) {
    ngx_http_request_t *sr;

    // 创建子请求
    sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));

    // 继承主请求的配置
    sr->main_conf = r->main_conf;
    sr->srv_conf = r->srv_conf;
    sr->loc_conf = r->loc_conf;

    // 设置子请求URI
    sr->uri = *uri;
    sr->args = args ? *args : ngx_null_string;

    // 子请求共享主请求的连接
    sr->connection = r->connection;

    // 增加引用计数
    sr->count = 1;
    r->count++;

    // 设置子请求处理器
    if (handler) {
        sr->post_subrequest = handler;
    }

    // 注册到主请求的子请求链表
    if (r->main->subrequests == 0) {
        // 超过子请求限制(50个)
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "subrequests cycle");
        return NGX_ERROR;
    }
    r->main->subrequests--;

    *psr = sr;

    return NGX_OK;
}

7.2 子请求的限制

  • 最大50个子请求(NGX_HTTP_MAX_SUBREQUESTS 50
  • 子请求不能修改主请求的URI(除非使用内部跳转)
  • 子请求共享主请求的连接和内存池
  • 子请求的响应通过过滤链合并到主请求

7.3 子请求vs直接HTTP请求

特性 子请求 直接HTTP请求
创建方式 ngx_http_subrequest() ngx_http_upstream_init()
连接 共享主请求的连接 独立的TCP连接
内存池 共享主请求的pool 独立的pool
配置 使用主请求的配置 使用独立的配置
响应 合并到主请求 独立响应
性能 高效(无TCP开销) 较低(需要TCP三次握手)
场景 SSI、模板包含 外部API调用

8. 本篇小结

概念 要点
请求流程 accept -> 解析行 -> 解析头 -> 11阶段 -> finalize
11阶段 POST_READ到LOG_PHASE,每阶段有checker+handler
CONTENT阶段 最重要,proxy_pass/fastcgi/static在此
checker 决定阶段如何推进(generic/rewrite/access/content)
子请求 SSI等场景使用,最大50个
内存池 每个请求一个pool,请求结束自动释放
引用计数 count管理请求生命周期

思考题

  1. 为什么 FIND_CONFIG 阶段不可配置?这对架构有什么好处?
  2. limit_req 在 PREACCESS 而非 ACCESS 阶段,为什么?
  3. 如果一个请求需要同时代理到3个后端服务,如何实现?
  4. NGINX的子请求和直接发起新HTTP请求有什么区别?

思考题解答

1. 为什么FIND_CONFIG阶段不可配置?

原因

FIND_CONFIG阶段的核心任务是将URI匹配到对应的location配置块。这是NGINX配置系统的基础操作,不能被用户代码干扰。

架构好处

  1. 确定性:location匹配的结果决定了后续所有阶段使用哪个配置,必须在阶段2完成,不能被模块延迟或修改。

  2. 隔离性:用户代码(rewrite规则等)不能破坏配置查找逻辑。如果允许自定义handler,可能导致配置系统混乱。

  3. 性能优化:NGINX可以在此阶段预计算匹配结果,后续阶段直接使用,避免重复匹配。

  4. 内部跳转支持:rewrite指令可能改变URI,导致需要重新匹配location。FIND_CONFIG阶段处理这种内部跳转。

实现

// src/http/ngx_http_core_module.c
static ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r,
    ngx_http_phase_handler_t *ph) {
    // 查找匹配的location
    ngx_http_core_find_location(r);

    // 设置r->loc_conf
    r->loc_conf = r->loc_conf;

    // 检查client_max_body_size
    if (r->headers_in.content_length_n > clcf->client_max_body_size) {
        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);
        return NGX_OK;
    }

    // 处理内部跳转
    if (rc == NGX_DONE) {
        // 返回301重定向
        ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);
        return NGX_OK;
    }

    // 继续下一阶段
    r->phase_handler++;
    return NGX_AGAIN;
}

2. limit_req在PREACCESS而非ACCESS阶段,为什么?

原因:限流应该在访问控制之前执行,因为:

  1. 先限流后认证:如果先做认证再限流,攻击者可以通过大量无效认证请求消耗服务器资源。限流应该在最前面,拒绝超过速率的请求。

  2. 模块职责分离

    • PREACCESS:限流、限连接数(基于速率的控制)
    • ACCESS:认证、授权(基于身份的控制)
  3. 避免无效计算:如果请求被限流拒绝,就不需要执行后续的认证、授权等操作,节省CPU。

  4. 配置语义limit_req_zonelimit_req 指令在PREACCESS阶段工作,与 allow/deny 的ACCESS阶段逻辑清晰分离。

典型流程

请求到达
  -> PREACCESS:limit_req检查 → 503 返回
  -> ACCESS:allow/deny检查 → 403 返回
  -> CONTENT:正常处理

3. 如何同时代理到3个后端服务?

方法1:子请求(Subrequest)

// 在content handler中创建子请求
ngx_http_subrequest(r, &uri, NULL, &sr, NULL, flags);

方法2:使用mirror模块

location /api {
    mirror /mirror1;
    mirror /mirror2;
    proxy_pass http://backend3;
}

location /mirror1 {
    proxy_pass http://backend1;
}

location /mirror2 {
    proxy_pass http://backend2;
}

方法3:使用upstream和lua

location /api {
    content_by_lua_block {
        local http = require "resty.http"
        -- 并发请求3个后端
        local res1, res2, res3 = 
            http.request("http://backend1/api"),
            http.request("http://backend2/api"),
            http.request("http://backend3/api")
        -- 合并结果
    }
}

方法4:使用SSI(Server Side Includes)

location /page {
    ssi on;
    # 页面中包含多个子请求
}

推荐方案:对于同步场景使用子请求,对于异步并发场景使用lua协程。

4. 子请求和直接HTTP请求的区别?

特性 子请求 直接HTTP请求
创建方式 ngx_http_subrequest() ngx_http_upstream_init()
连接 共享主请求的连接 独立的TCP连接
内存池 共享主请求的pool 独立的pool
配置 使用主请求的配置 使用独立的配置
响应 合并到主请求 独立响应
性能 高效(无TCP开销) 较低(需要TCP三次握手)
场景 SSI、模板包含 外部API调用

子请求的优势

  • 不需要建立新的TCP连接
  • 不需要DNS解析
  • 可以共享主请求的连接池和缓存
  • 响应直接写入主请求的过滤链

子请求的限制

  • 不能跨域(必须在同一server内)
  • 不能修改主请求的URI(除非使用内部跳转)
  • 子请求的响应格式必须与主请求兼容

实际应用

  • SSI(Server Side Includes):在HTML中包含其他URL的内容
  • 模板系统:将页面拆分为多个子请求
  • API聚合:将多个内部API调用合并为一个外部请求
posted @ 2026-09-04 17:26  IcarusLee  阅读(2)  评论(0)    收藏  举报