作为技术顾问,深入理解这 11 个阶段是掌握 Nginx 性能优化的“金钥匙”。Nginx 采用的是阶段式处理(Phased Processing),请求就像在流水线上一样,依次经过这些工位。

对于移动开发(特别是使用 OpenResty)来说,你不需要记住每一个,但其中有 4-5 个阶段是决定业务逻辑和性能的关键


Nginx 请求处理的 11 个阶段

按照执行顺序,它们分别是:

  1. Post-Read:读取完请求头后的第一个阶段(如:支持 realip 模块获取客户端真实 IP)。
  2. Rewrite关键):执行 rewrite 指令,进行 URL 重写。
  3. Post-Rewrite:URL 重写后的清理阶段,防止死循环。
  4. Pre-Access:访问权限检查前的准备(如:limit_conn 限制连接数、limit_req 限制频率)。
  5. Access核心):验证用户是否有权限访问(如:IP 黑白名单、账号登录校验)。
  6. Post-Access:权限检查后的清理,处理 Access 阶段返回的拒绝结果。
  7. Pre-Content:生成内容前的准备(如:try_files 检查文件是否存在)。
  8. Content核心中的核心):响应生成阶段。产生 Body 数据,或者转发给上游(Proxy Pass)。
  9. Log关键):请求结束后的记录。
  10. Filter(严格说是两个阶段:Header Filter 和 Body Filter):对响应结果进行最后的加工(如:Gzip 压缩、替换响应内容)。

资深顾问:你需要重点关注的 5 个关键阶段

在 OpenResty 开发中,我们 90% 的代码都集中在以下几个钩子中:

阶段 对应的 OpenResty 指令 核心用途 (移动端场景) 顾问建议
Rewrite rewrite_by_lua 动态路由/改写:根据 App 版本号动态将请求分发给 v1 或 v2 接口。 适合做轻量级的参数预处理。
Access access_by_lua 安全防火墙/鉴权:校验 JWT Token、验证签名、拦截刷票脚本。 性能杀手:此阶段逻辑若慢,整个请求就慢。务必用 Redis 非阻塞查询。
Content content_by_lua 业务输出:直接返回 JSON。或者做“合并请求”(同时调用 3 个微服务并聚合结果)。 这是 Nginx 的“终点站”,一旦命中就不会再去执行 proxy_pass
Filter body_filter_by_lua 实时篡改:在下发响应给手机前,动态注入调试信息或敏感词过滤。 慎用!处理大文件流时非常消耗内存和 CPU。
Log log_by_lua 数据埋点:将 API 耗时、状态码异步发送到 ELK 或统计后台。 它是异步的,不影响用户响应速度,非常适合做监控。

批判性思维:为什么要分这么多阶段?

你可能会问:“为什么不直接一个 handler 搞定所有事?”

  1. 解耦与复用:比如“限流”逻辑是在 Pre-Access 做的。如果权限校验(Access)不通过,直接在前面就拦截了,根本不需要走到后面的业务逻辑(Content),极大地节省了 CPU 资源
  2. 细粒度控制:作为移动端后端,你可能希望“先限流,再验签,最后读缓存”。这 11 个阶段给了你精确控制这种时序的能力。

落地导向:代码示例

假设你要实现一个移动端动态 API 网关,你的 nginx.conf 逻辑通常是这样的:

location /api/user/info {
    # 1. 频率限制 (Pre-Access阶段)
    limit_req zone=one burst=5 nodelay;

    # 2. 身份校验 (Access阶段)
    access_by_lua_block {
        local auth = require "my_auth"
        auth.check_token() -- 校验不通过直接 ngx.exit(401)
    }

    # 3. 业务逻辑 (Content阶段)
    # 方案 A: 转发给 Java 后端
    proxy_pass http://java_backend;

    # 方案 B: 或者直接由 Lua 处理返回缓存 (如果开启了 content_by_lua,proxy_pass 会失效)
    # content_by_lua_block { 
    #    ngx.say('{"status":"ok"}') 
    # }

    # 4. 日志统计 (Log阶段)
    log_by_lua_block {
        local stats = require "my_stats"
        stats.report_latency() -- 统计接口耗时
    }
}

顾问提问:

了解了这 11 个阶段后,你是否已经有了具体的业务场景想尝试?

例如:你是想在 Access 阶段 做一套通用的 App 签名校验,还是想在 Content 阶段 实现一个高性能的配置下发接口?如果你有具体的代码逻辑,我可以帮你审查一下它应该放在哪个阶段最合适。