作为技术顾问,深入理解这 11 个阶段是掌握 Nginx 性能优化的“金钥匙”。Nginx 采用的是阶段式处理(Phased Processing),请求就像在流水线上一样,依次经过这些工位。
对于移动开发(特别是使用 OpenResty)来说,你不需要记住每一个,但其中有 4-5 个阶段是决定业务逻辑和性能的关键。
Nginx 请求处理的 11 个阶段
按照执行顺序,它们分别是:
Post-Read:读取完请求头后的第一个阶段(如:支持realip模块获取客户端真实 IP)。Rewrite(关键):执行rewrite指令,进行 URL 重写。Post-Rewrite:URL 重写后的清理阶段,防止死循环。Pre-Access:访问权限检查前的准备(如:limit_conn限制连接数、limit_req限制频率)。Access(核心):验证用户是否有权限访问(如:IP 黑白名单、账号登录校验)。Post-Access:权限检查后的清理,处理 Access 阶段返回的拒绝结果。Pre-Content:生成内容前的准备(如:try_files检查文件是否存在)。Content(核心中的核心):响应生成阶段。产生 Body 数据,或者转发给上游(Proxy Pass)。Log(关键):请求结束后的记录。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 搞定所有事?”
- 解耦与复用:比如“限流”逻辑是在
Pre-Access做的。如果权限校验(Access)不通过,直接在前面就拦截了,根本不需要走到后面的业务逻辑(Content),极大地节省了 CPU 资源。 - 细粒度控制:作为移动端后端,你可能希望“先限流,再验签,最后读缓存”。这 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 阶段 实现一个高性能的配置下发接口?如果你有具体的代码逻辑,我可以帮你审查一下它应该放在哪个阶段最合适。