当前位置: 首页 > news >正文

6. 深入 Nginx 核心:HTTP 11 个处理阶段与模块开发实战

1. 引言

Nginx 以其高性能、高并发和模块化架构闻名。对于开发者而言,理解 Nginx 处理 HTTP 请求的完整生命周期——即11 个处理阶段(Phases)——是编写高效、正确模块的基石。

本文将深入剖析这 11 个阶段,结合 Nginx 源码,并通过一个完整的实战示例,带你从零开发一个可独立加载的 Nginx 模块,助你真正掌握 Nginx 模块开发的精髓。

2. Nginx 模块与阶段概览

2.1 模块化架构

Nginx 本身是一个轻量级的核心,大部分功能(如 HTTP 处理、反向代理、SSL 等)都由模块提供。模块通过钩子(Handlers)和过滤器(Filters)介入请求处理流程。

2.2 11 个处理阶段

下面这张流程图直观地展示了这 11 个阶段的执行顺序、关键分支(如重写后循环)以及核心阶段的产出:

NGX_HTTP_POST_READ_PHASE
读取请求头后

NGX_HTTP_SERVER_REWRITE_PHASE
Server 级别 URI 重写

NGX_HTTP_FIND_CONFIG_PHASE
查找 Location 配置

NGX_HTTP_REWRITE_PHASE
Location 级别 URI 重写

NGX_HTTP_POST_REWRITE_PHASE
URI 是否发生变化?

NGX_HTTP_PREACCESS_PHASE
访问控制前(限流/限速)

NGX_HTTP_ACCESS_PHASE
访问控制(IP黑白名单/认证)

NGX_HTTP_POST_ACCESS_PHASE
访问控制后(403处理)

NGX_HTTP_PRECONTENT_PHASE
生成内容前(try_files/autoindex)

NGX_HTTP_CONTENT_PHASE
生成内容(核心阶段)
产出:HTTP 响应体

NGX_HTTP_LOG_PHASE
日志记录

Nginx 将 HTTP 请求的处理过程划分为 11 个有序的阶段,定义在src/http/ngx_http_core_module.h中:

typedefenum{NGX_HTTP_POST_READ_PHASE=0,// 读取请求头后的阶段NGX_HTTP_SERVER_REWRITE_PHASE,// Server 级别 URI 重写NGX_HTTP_FIND_CONFIG_PHASE,// 查找 Location 配置NGX_HTTP_REWRITE_PHASE,// Location 级别 URI 重写NGX_HTTP_POST_REWRITE_PHASE,// URI 重写后的检查阶段NGX_HTTP_PREACCESS_PHASE,// 访问控制前阶段(如限流)NGX_HTTP_ACCESS_PHASE,// 访问控制阶段(如 IP 黑白名单)NGX_HTTP_POST_ACCESS_PHASE,// 访问控制后阶段NGX_HTTP_PRECONTENT_PHASE,// 生成内容前阶段NGX_HTTP_CONTENT_PHASE,// 生成内容阶段(核心)NGX_HTTP_LOG_PHASE// 日志记录阶段}ngx_http_phases;

这些阶段按顺序执行,每个阶段可以注册多个处理函数(Handler),它们会依次被调用,直至某个函数返回NGX_OKNGX_DECLINED

3. 各阶段源码级解析

3.1NGX_HTTP_POST_READ_PHASE

  • 时机:Nginx 读取完 HTTP 请求的头部(Header)之后,但在进行任何其他处理之前。
  • 典型用途:解析自定义请求头、记录原始请求信息。
  • 源码位置src/http/ngx_http_request.c中的ngx_http_process_request()函数。
// 简化后的调用逻辑voidngx_http_process_request(ngx_http_request_t*r){// ... 读取并解析请求头 ...if(ngx_http_process_request_header(r)!=NGX_OK){return;}// 执行 POST_READ 阶段的处理器ngx_http_handler(r,NGX_HTTP_POST_READ_PHASE);// ... 后续处理 ...}

3.2NGX_HTTP_SERVER_REWRITE_PHASE

  • 时机:在server块内,根据server_name匹配到虚拟服务器后,执行server块内的rewrite指令。
  • 典型用途:基于域名或全局规则进行 URL 重写。
  • 源码位置src/http/ngx_http_rewrite_module.c

3.3NGX_HTTP_FIND_CONFIG_PHASE

  • 时机:这是一个内部阶段,用于根据请求的 URI 查找匹配的location块。
  • 典型用途:开发者通常不在此阶段注册处理器,Nginx 内部使用。
  • 源码位置src/http/ngx_http_core_module.c中的ngx_http_core_find_config_phase()

3.4NGX_HTTP_REWRITE_PHASE

  • 时机:在匹配到具体的location块后,执行该location块内的rewrite指令。
  • 典型用途:对特定路径的请求进行 URL 重写。

3.5NGX_HTTP_POST_REWRITE_PHASE

  • 时机REWRITE_PHASE执行完毕后。如果之前的重写导致 URI 发生变化,Nginx 会在此阶段重新执行FIND_CONFIG_PHASE以匹配新的location
  • 典型用途:防止重写后的循环,并进行最终检查。

3.6NGX_HTTP_PREACCESS_PHASE

  • 时机:在正式的访问控制阶段检查之前。
  • 典型用途:实现连接数限制(ngx_http_limit_conn_module)、请求频率限制(ngx_http_limit_req_module)。
  • 源码位置src/http/modules/ngx_http_limit_conn_module.c

3.7NGX_HTTP_ACCESS_PHASE

  • 时机:执行访问控制逻辑。
  • 典型用途:IP 黑白名单(ngx_http_access_module)、用户认证(ngx_http_auth_basic_module)。
  • 源码位置src/http/modules/ngx_http_access_module.c

3.8NGX_HTTP_POST_ACCESS_PHASE

  • 时机ACCESS_PHASE执行完毕后。如果访问被拒绝,此阶段会发送403 Forbidden响应。
  • 典型用途:处理访问控制阶段后的收尾工作。

3.9NGX_HTTP_PRECONTENT_PHASE

  • 时机:在生成响应内容之前。
  • 典型用途:处理try_files指令、生成静态文件索引(ngx_http_autoindex_module)、处理mirror请求。
  • 源码位置src/http/modules/ngx_http_try_files_module.c

3.10NGX_HTTP_CONTENT_PHASE

  • 时机核心阶段,负责生成 HTTP 响应体。
  • 典型用途:代理请求(ngx_http_proxy_module)、FastCGI(ngx_http_fastcgi_module)、返回静态文件(ngx_http_static_module)。
  • 源码位置src/http/ngx_http_core_module.c中的ngx_http_core_content_phase()
// 核心内容处理逻辑ngx_int_tngx_http_core_content_phase(ngx_http_request_t*r,ngx_http_phase_handler_t*ph){// 查找并执行注册的 content handlerngx_http_handler_pt handler=r->content_handler;if(handler){returnhandler(r);}// 如果没有自定义 handler,则尝试默认处理(如静态文件)returnNGX_DECLINED;}

3.11NGX_HTTP_LOG_PHASE

  • 时机:请求处理完毕,准备记录日志时。
  • 典型用途:自定义日志格式、记录请求处理耗时等。
  • 源码位置src/http/modules/ngx_http_log_module.c

4. 实战:开发一个独立的 Nginx 模块

我们将开发一个名为ngx_http_hello_phase_module的模块,它会在REWRITE_PHASEACCESS_PHASE中插入自定义处理逻辑,并通过独立的config文件加载。

4.1 项目结构

nginx-hello-phase-module/ ├── config └── ngx_http_hello_phase_module.c

4.2 模块源码:ngx_http_hello_phase_module.c

#include<ngx_config.h>#include<ngx_core.h>#include<ngx_http.h>// 模块上下文定义staticngx_int_tngx_http_hello_phase_handler(ngx_http_request_t*r);staticngx_int_tngx_http_hello_access_handler(ngx_http_request_t*r);staticchar*ngx_http_hello_phase(ngx_conf_t*cf,ngx_command_t*cmd,void*conf);// 指令定义staticngx_command_tngx_http_hello_phase_commands[]={{ngx_string("hello_phase"),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,ngx_http_hello_phase,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command};// 模块配置结构typedefstruct{ngx_flag_tenable;}ngx_http_hello_phase_loc_conf_t;// 创建 location 配置staticvoid*ngx_http_hello_phase_create_loc_conf(ngx_conf_t*cf){ngx_http_hello_phase_loc_conf_t*conf;conf=ngx_pcalloc(cf->pool,sizeof(ngx_http_hello_phase_loc_conf_t));if(conf==NULL){returnNULL;}conf->enable=NGX_CONF_UNSET;returnconf;}// 合并 location 配置staticchar*ngx_http_hello_phase_merge_loc_conf(ngx_conf_t*cf,void*parent,void*child){ngx_http_hello_phase_loc_conf_t*prev=parent;ngx_http_hello_phase_loc_conf_t*conf=child;ngx_conf_merge_value(conf->enable,prev->enable,0);returnNGX_CONF_OK;}// 指令处理函数:解析 hello_phase 指令,并注册处理器staticchar*ngx_http_hello_phase(ngx_conf_t*cf,ngx_command_t*cmd,void*conf){ngx_http_core_loc_conf_t*clcf;ngx_http_hello_phase_loc_conf_t*hlcf=conf;// 解析指令值char*rv=ngx_conf_set_flag_slot(cf,cmd,conf);if(rv!=NGX_CONF_OK){returnrv;}// 如果指令值为 on,则注册处理器if(hlcf->enable){// 获取当前 location 的核心配置clcf=ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module);// 注册一个处理器到 REWRITE_PHASEngx_http_handler_pt*h=ngx_array_push(&clcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);if(h==NULL){returnNGX_CONF_ERROR;}*h=ngx_http_hello_phase_handler;// 注册一个处理器到 ACCESS_PHASEh=ngx_array_push(&clcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);if(h==NULL){returnNGX_CONF_ERROR;}*h=ngx_http_hello_access_handler;}returnNGX_CONF_OK;}// REWRITE_PHASE 处理器staticngx_int_tngx_http_hello_phase_handler(ngx_http_request_t*r){ngx_log_error(NGX_LOG_INFO,r->connection->log,0,"hello_phase: entering REWRITE_PHASE for URI: %V",&r->uri);// 这里可以添加自定义的重写逻辑// 返回 NGX_DECLINED 让后续处理器继续执行returnNGX_DECLINED;}// ACCESS_PHASE 处理器staticngx_int_tngx_http_hello_access_handler(ngx_http_request_t*r){ngx_log_error(NGX_LOG_INFO,r->connection->log,0,"hello_phase: entering ACCESS_PHASE for URI: %V",&r->uri);// 示例:拒绝访问 /secret 路径if(ngx_strncmp(r->uri.data,"/secret",7)==0){returnNGX_HTTP_FORBIDDEN;}returnNGX_DECLINED;}// 模块上下文staticngx_http_module_tngx_http_hello_phase_module_ctx={NULL,// preconfigurationNULL,// postconfigurationNULL,// create main configurationNULL,// init main configurationNULL,// create server configurationNULL,// merge server configurationngx_http_hello_phase_create_loc_conf,// create location configurationngx_http_hello_phase_merge_loc_conf// merge location configuration};// 模块定义ngx_module_tngx_http_hello_phase_module={NGX_MODULE_V1,&ngx_http_hello_phase_module_ctx,// module contextngx_http_hello_phase_commands,// module directivesNGX_HTTP_MODULE,// module typeNULL,// init masterNULL,// init moduleNULL,// init processNULL,// init threadNULL,// exit threadNULL,// exit processNULL,// exit masterNGX_MODULE_V1_PADDING};

4.3 独立配置文件:config

ngx_addon_name=ngx_http_hello_phase_module HTTP_MODULES="$HTTP_MODULES ngx_http_hello_phase_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_phase_module.c"

4.4 编译与加载

  1. 编译为动态模块(推荐)

    ./configure --add-dynamic-module=/path/to/nginx-hello-phase-modulemakemodulessudomakeinstall
  2. nginx.conf中加载

    load_module modules/ngx_http_hello_phase_module.so; http { server { listen 80; server_name example.com; location / { hello_phase on; # ... 其他配置 ... } location /secret { hello_phase on; # 访问 /secret 会被我们的模块拒绝 } } }

5. 总结

通过本文,我们深入了解了 Nginx HTTP 处理的 11 个阶段,并结合源码分析了每个阶段的作用。实战部分,我们开发了一个独立的 Nginx 模块,演示了如何在REWRITE_PHASEACCESS_PHASE中注册自定义处理器,并通过config文件实现独立加载。

掌握这些知识,你将能够编写出更强大、更贴合业务需求的 Nginx 模块,充分释放 Nginx 的潜力。

http://www.gsyq.cn/news/1617961.html

相关文章:

  • 【2026年华为暑期实习(AI)-7月1日-第三题- Certainty Forcing 训练损失计算】(题目+思路+JavaC++Python解析+在线测试)
  • AI 辅助:前端工程化效率:快不是少检查,而是少返工
  • 深度学习Pipeline与Baseline构建指南
  • 截屏、OCR、翻译、录屏全打包?这款开源软件,一个快捷键搞定所有!
  • 工程化赋能传统业务工作流:先找重复劳动,不要先找服务
  • SpringBoot 自动配置原理
  • 死磕信号量实现读者-写者:我被自己写的代码坑惨了
  • Xinference开源大模型本地部署实战指南
  • UABEA:重新定义Unity资源编辑的跨平台革命
  • 大厂高频面试题:手机号加密存储后,如何快速按尾号查询?
  • 终极Windows驱动管理指南:DriverStoreExplorer免费释放C盘空间
  • 为了防止题目链接失效,将题目原文复制如下:
  • Java实现Navicat密码加密解密:AES-256-CBC本地安全存储实战
  • QuickVina 2深度解析:20倍加速的分子对接性能揭秘
  • Go 进阶必修:90% 的人都没用对的“表驱动法”
  • 关于动态规划【力扣300.最长递增子序列的思考】
  • 华为MetaERP Oracle EBS R12 AP 供应商主数据完整配置指南(架构师实施版)一、前置基础配置(必须先完成,否则供应商无法正常使用)(一)财务选项 Financials Opti
  • 给制造以光,让智造有根:中策橡胶卓越智能工厂背后的F5G-A全光力量
  • 基于树莓派的边缘计算安全网关设计与实现
  • 2026燃油车底盘整备调校,选对修理厂事半功倍
  • 5分钟学会免费音乐解锁:打破平台限制的完整指南
  • Walmart SDE Interview Experience 三轮 VO 高频面经 | System Design + BQ + 算法 稳稳拿 Offer(2026)
  • 【第 9 篇:本地化部署——从 0 到 1 的企业级系统部署全记录】
  • 导师严选!盘点2026年备受推崇的的AI智能降重工具
  • Linux基础文件与目录命令实操实验报告
  • FPG财盛国际:围绕服务体系与外汇用户支持体系的路径解读
  • 零API费用的金融AI技能库:104个场景纯Python实现,毫秒级响应
  • DVWA 靶场 SQL 注入实战心得:从手工检测到布尔盲注自动化利用全流程详解
  • 2026广州高端宣传片拍摄团队怎么选?广州AIGC企业视频制作机构盘点
  • 还在手敲数据库三线表?这个SQL自动生成法,建议直接收藏!