Nginx源码分析 - HTTP模块篇 - HTTP模块的阶段处理PHASE handler(23)
目录
一、HTTP模块11个阶段处理
二、阶段处理的初始化ngx_http_block
三、阶段处理过程ngx_http_core_run_phases
四、挂载自定义模块
上一章我们讲解了HTTP request的解析过程。我们基本知道了Nginx是如何解析HTTP的行和头。
这一章我们主要讲解Nginx的阶段处理(PHASE 状态机实现)。阶段处理的概念如何理解,我举个例子:一个HTTP请求过来,除了解析HTTP的行和头外,还需要解析URI的rewrite、接受HTTP BODY中的POST数据、转发给后端JAVA/PHP服务器进行数据处理等等一系列的操作。
Nginx的PHASE阶段处理共包含11部分,通过这11个阶段的处理,就能完整的处理一个HTTP请求了。
一、HTTP模块11个阶段处理
typedef enum {NGX_HTTP_POST_READ_PHASE = 0, /* 读取请求内容阶段 */NGX_HTTP_SERVER_REWRITE_PHASE, /* Server请求地址重写阶段 */NGX_HTTP_FIND_CONFIG_PHASE, /* 配置查找阶段 */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_TRY_FILES_PHASE, /* 配置项try_files处理阶段 */NGX_HTTP_CONTENT_PHASE, /* 内容产生阶段 */NGX_HTTP_LOG_PHASE /* 日志模块处理阶段 */
} ngx_http_phases;
四个phase是不支持添加http功能的handler的,这四个阶段分别是NGX_FIND_CONFIG_PHASE、NGX_POST_REWRITE_PHASE、NGX_POST_ACCESS_PHASE、NGX_TRY_FILES_PHASE
如果需要自定义添加模块挂载的Nginx的阶段处理上,推荐挂载到这三个阶段:NGX_HTTP_PREACCESS_PHASE、NGX_HTTP_ACCESS_PHASE、NGX_HTTP_CONTENT_PHASE
二、阶段处理的初始化ngx_http_block
我们从前面的篇章中知道,HTTP模块的初始化从 ngx_http_block这个方法开始。阶段处理的初始化也在这个方法中。
/***ngx_http_commands 命令集的回调函数*HTTP模块初始化的入口函数**/
static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
........./* 初始化阶段数组 只有这里面的6个阶段可以挂载模块 */if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {return NGX_CONF_ERROR;}
......../*** 初始化阶段处理*/if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {return NGX_CONF_ERROR;}
}
ngx_http_init_phases 初始化阶段数组
/*** 初始化阶段处理数组* 每一个可以挂载模块的阶段,都定义了一个cmcf->phases[?].handlers的数组* 每个阶段被调用的时候,都会去遍历改阶段处理数组下需要处理的逻辑函数*/
static ngx_int_t
ngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,cf->pool, 1, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,cf->pool, 1, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,cf->pool, 1, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,cf->pool, 1, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,cf->pool, 2, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,cf->pool, 4, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,cf->pool, 1, sizeof(ngx_http_handler_pt))!= NGX_OK){return NGX_ERROR;}return NGX_OK;
}
ngx_http_init_phase_handlers 初始化阶段处理
/*** 初始化阶段处理** nginx在接收并解析完请求行,请求头之后,就会依次调用各个phase handler** NGX_HTTP_POST_READ_PHASE 读取请求内容阶段NGX_HTTP_SERVER_REWRITE_PHASE Server请求地址重写阶段NGX_HTTP_FIND_CONFIG_PHASE 配置查找阶段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_TRY_FILES_PHASE 配置项try_files处理阶段NGX_HTTP_CONTENT_PHASE 内容产生阶段NGX_HTTP_LOG_PHASE 日志模块处理阶段*/
static ngx_int_t
ngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)
{ngx_int_t j;ngx_uint_t i, n;ngx_uint_t find_config_index, use_rewrite, use_access;ngx_http_handler_pt *h;ngx_http_phase_handler_t *ph;ngx_http_phase_handler_pt checker;cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;find_config_index = 0;use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */;for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {n += cmcf->phases[i].handlers.nelts;}ph = ngx_pcalloc(cf->pool,n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));if (ph == NULL) {return NGX_ERROR;}cmcf->phase_engine.handlers = ph;n = 0;for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {h = cmcf->phases[i].handlers.elts;switch (i) {/* server中的rewrite*/case NGX_HTTP_SERVER_REWRITE_PHASE:if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {cmcf->phase_engine.server_rewrite_index = n;}checker = ngx_http_core_rewrite_phase;break;/* 根据URI查找 location */case NGX_HTTP_FIND_CONFIG_PHASE:find_config_index = n;ph->checker = ngx_http_core_find_config_phase;n++;ph++;continue;/* localtion级别的rewrite */case NGX_HTTP_REWRITE_PHASE:if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {cmcf->phase_engine.location_rewrite_index = n;}checker = ngx_http_core_rewrite_phase;break;/* server、location级别的rewrite都是在这个phase进行收尾工作的 */case NGX_HTTP_POST_REWRITE_PHASE:if (use_rewrite) {ph->checker = ngx_http_core_post_rewrite_phase;ph->next = find_config_index;n++;ph++;}continue;/* 细粒度的access,比如权限验证、存取控制 */case NGX_HTTP_ACCESS_PHASE:checker = ngx_http_core_access_phase;n++;break;/* 根据上述两个phase得到access code进行操作 */case NGX_HTTP_POST_ACCESS_PHASE:if (use_access) {ph->checker = ngx_http_core_post_access_phase;ph->next = n;ph++;}continue;/* 实现try_files指令 */case NGX_HTTP_TRY_FILES_PHASE:if (cmcf->try_files) {ph->checker = ngx_http_core_try_files_phase;n++;ph++;}continue;/* 生成http响应 */case NGX_HTTP_CONTENT_PHASE:checker = ngx_http_core_content_phase;break;default:checker = ngx_http_core_generic_phase;}n += cmcf->phases[i].handlers.nelts;for (j = cmcf->phases[i].handlers.nelts - 1; j >=0; j--) {ph->checker = checker;ph->handler = h[j];ph->next = n;ph++;}}return NGX_OK;
}
三、阶段处理过程ngx_http_core_run_phases
阶段处理核心函数ngx_http_core_run_phases
先看下阶段处理返回的几个状态含义:
NGX_OK: 表示该阶段已经处理完成,需要转入下一个阶段;
NGX_DECLINED: 表示需要转入本阶段的下一个handler继续处理;
NGX_AGAIN, NGX_DONE:表示需要等待某个事件发生才能继续处理(比如等待网络IO),此时Nginx为了不阻塞其他请求的处理,必须中断当前请求的执行链,等待事件发生之后继续执行该handler;
NGX_ERROR: 表示发生了错误,需要结束该请求。
/*** 11个阶段处理HTTP请求*/
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{ngx_int_t rc;ngx_http_phase_handler_t *ph;ngx_http_core_main_conf_t *cmcf;cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);ph = cmcf->phase_engine.handlers;/* 遍历解析和处理各个阶段的HTTP请求 如果返回rc==NGX_AGAIN 则交由下一个阶段处理;返回OK则返回结果 */while (ph[r->phase_handler].checker) {rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);if (rc == NGX_OK) {return;}}
}/*** 内容接收阶段*/
ngx_int_t
ngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{ngx_int_t rc;/** generic phase checker,* used by the post read and pre-access phases*/ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"generic phase: %ui", r->phase_handler);/* handler 回调函数*/rc = ph->handler(r);/* 本阶段处理完成,跳转到下一个阶段处理 */if (rc == NGX_OK) {r->phase_handler = ph->next;return NGX_AGAIN;}/* 本阶段当前的回调函数处理完成,继续执行本阶段其他回调函数 */if (rc == NGX_DECLINED) {r->phase_handler++;return NGX_AGAIN;}if (rc == NGX_AGAIN || rc == NGX_DONE) {return NGX_OK;}/* rc == NGX_ERROR || rc == NGX_HTTP_... */ngx_http_finalize_request(r, rc);return NGX_OK;
}
四、挂载自定义模块
Nginx的自定义模块在http/modules/目录下。如果你编写了一个模块,并且想在阶段处理中,编写自己的模块,那么就非常简单了。只要在模块init初始化的时候,将回调函数注册到阶段上,就能实现自定义阶段处理拦截。
我们看下ngx_http_static_module.c模块的阶段处理的注册。
/*** 模块初始化*/
static ngx_int_t
ngx_http_static_init(ngx_conf_t *cf)
{ngx_http_handler_pt *h;ngx_http_core_main_conf_t *cmcf;cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);/* 注册到NGX_HTTP_CONTENT_PHASE阶段 */h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);if (h == NULL) {return NGX_ERROR;}/* 设置阶段回调函数 */*h = ngx_http_static_handler;return NGX_OK;
}
以上就是整个阶段处理的过程。理解之后发现并不难,而且可扩展性非常强,非常适合在不同阶段中挂载自定义的Nginx模块代码。
下一章,也是最后一章,我们讲解一下自定义模块的实现。
转载地址:
1. https://initphp.blog.csdn.net/article/details/72899546
Nginx源码分析 - HTTP模块篇 - HTTP模块的阶段处理PHASE handler(23)相关推荐
- nginx源码分析(2)——http模块的初始化过程
前一篇文章介绍了nginx的启动初始化过程,包括了所有模块的初始化过程,比如http模块.事件模块等.这里再详细介绍一下http模块的启动过程,还记得在前一篇文章中提到过ngx_conf_parse函 ...
- Nginx 源码分析
1.工程 ngx_conf_file.c ngx_connection.c ngx_cycle.c ngx_file.h ngx_module.c ngx_open_file_cache.h ngx_ ...
- Nginx源码分析:epoll事件处理模块概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 事件处理模块概述 Nginx的高效请求的处理依赖于事件管理机制,本次默认的场景是Linux操 ...
- Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一
搭建nginx服务器时,主要的配置文件 nginx.conf 是部署和维护服务器人员经常要使用到的文件, 里面进行了许多服务器参数的设置.那么nginx 以模块 module为骨架的设计下是如何运用模 ...
- nginx源码分析(5)——监听socket初始化
在nginx源码分析(4)中,看到了nginx的事件模型,但其中没有介绍监听socket的初始化.而对于web server来说,需要通过监听socket来监听客户端的连接等.本篇将会具体介绍这方面的 ...
- nginx源码分析之网络初始化
nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...
- Nginx源码分析:惊群处理与负载均衡
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的惊群处理与负载均衡概述 当Nginx工作在master/worker模式下时,就 ...
- Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...
- Nginx源码分析:master/worker工作流程概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...
- Nginx源码分析:启动流程
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> nginx简介 Nginx的作为服务端软件,表现的主要特点是更快.高扩展.高可靠性.低内存消 ...
最新文章
- 当输入 xxxxHub 后,到网页显示,其间发生了什么?
- nlm算法matlab代码_遗传算法GA的MATLAB代码
- 使用Spring 3.2的DeferredResult进行长轮询
- 云服务器带宽如何计算,云服务器怎么选择带宽
- Qt文档阅读笔记-Qt工作笔记-QThread解析与实例(主线程发送信号给子线程)
- 数字信号处理——巴特沃斯滤波器设计
- cacti监控mysql
- DNS DDNS NBNS mDNS LLMNR LLDPDU SSDP协议
- (原创)安卓抓包方案分享
- 有哪些公共管理或行政管理学习帮助较大的外文期刊?
- Web服务器及性能优化
- 看两宋风云,搞清了四个之前对两宋历史认识错误的地方
- 上海地铁二号线和一号线的差距
- URL Schemes 的发展
- 最强AngularJS资源合集
- Python写入数据到txt文本中
- AbMole推荐:人源化单抗动物实验黄金指南 (上)
- Unity导表工具Luban插件的数据加载原理与优化
- linux usleep占用cpu,c-Cent OS 6.3上的usleep的CPU高使用率
- 获取Excel单元格存储日期格式数据
热门文章
- Linux 下面解压.tar.gz 和.gz文件解压的方式
- HTML5 — 知识总结篇《VIII》【媒体元素】
- Android SwipeRefreshLayout 实现下拉刷新1
- SELECT中的多表连接
- 从P1到P7——我在淘宝这7年 - 子柳撰写
- 删除windows目录下的$NTUnintall$文件夹DOS命令
- mysql创建用户navicat_14MYSQL创建用户和授权、15Navicat的使用、16-pymysql模块的使用、17-索引...
- C/C++ 基础算法1
- Docker详解(十六)——Docker私有化仓库创建
- NYOJ--1100--WAJUEJI which home strong!