当前位置: 首页 > 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.jsqmd.com/news/1105988/

相关文章:

  • 轻量级AI模型实战:低配设备部署与优化指南
  • 【2026年华为暑期实习(AI)-7月1日-第三题- Certainty Forcing 训练损失计算】(题目+思路+JavaC++Python解析+在线测试)
  • 基于ICM-42605和GD32VF103的6DOF运动追踪系统设计
  • adb截图-------在小程序中实现纯 JS 驱动的 ADB 客户端
  • 输入输出流重载说明:std::ostream operator<<(std::ostream os, const Vector v)
  • AI 辅助:前端工程化效率:快不是少检查,而是少返工
  • Python在AI开发中的核心优势与实战技巧
  • 变分量子本征求解器(VQE)原理与NISQ设备应用
  • 深度学习Pipeline与Baseline构建指南
  • 【6.20】射频\+FPGA\+Verilog\+仪器自动化 完整知识链路复盘
  • 智能体时代,软件工程的本质
  • 现在系统运行基本上正常,较少遇到问题了
  • 采齿背后的能量闭包原理
  • 截屏、OCR、翻译、录屏全打包?这款开源软件,一个快捷键搞定所有!
  • OpenHarmony 英语学习 App 实战:从 0 到 1 搭建中小学生英语学习应用
  • 工程化赋能传统业务工作流:先找重复劳动,不要先找服务
  • 大模型评测与AI产品质量保障:第7篇 机器学习的三种学习范式
  • SQL实战:测试必会的增删改查,从入门到熟练
  • SpringBoot 自动配置原理
  • 记忆排列题目分析
  • 第93题 IGBT模块陶瓷基板(AlN/Al₂O₃/Si₃N₄)金属化
  • C++ PDF解析渲染库Poppler全方位实战:场景、库对比、CMake集成、可运行代码
  • 死磕信号量实现读者-写者:我被自己写的代码坑惨了
  • 市县级全域旅游智慧导览电子地图制作实操(三)AI+人工生成全域手绘地图
  • Xinference开源大模型本地部署实战指南
  • 工业级条码扫描模块与PIC32MZ嵌入式方案解析
  • 3分钟掌握Illustrator智能填充:Fillinger脚本让你的设计效率翻倍
  • 网络流量分类技术:从机器学习到硬件优化实践
  • UABEA:重新定义Unity资源编辑的跨平台革命
  • 迅雷网盘在线解析:高速直链下载的方法