ngx-modules

Nginx 基本的模块大致能够分为四类:

  • handler – 协同完毕client请求的处理、产生响应数据。比方模块, ngx_http_rewrite_module,
    ngx_http_log_module
    , ngx_http_static_module

  • filter – 对 handler 产生的响应数据做各种过滤处理。

    比方模块, ngx_http_not_modified_filter_module,
    ngx_http_header_filter_module

  • upstream – upstream 模块使 Nginx 能够充当反向代理,仅仅对client请求进行转发。

    比 如,ngx_http_proxy_module,
    ngx_http_fastcgi_module

  • loadbalance – 在 Nginx 充当反向代理时,通过 loadbalance 模块的策略选择处理某次 请求的后端server节点。

比方。对一个普通的訪问本地静态文件的请求处理,从 Nginx 收到请求并開始处理。到处 理结果的响应包体发送到网络上结束,整个过程的两个步骤 – 请求处理和响应处理 – 分别 由 handler 和 filter 处理完毕。

严格来说,upstream 模块 (proxy, fastcgi 等),也是请求处理的一部分,它们从网络获 取请求的响应内容。

以下仅仅分析一下 handler 模块和 filter 模块的注冊和调用逻辑。

handler 模块

Nginx 将请求的处理阶段分为了以下列出的11个 phase 。假设某个 handler 模块须要在某 个 phase 中被调用时,须要在 postconfiguration 时将 handler 模块的入口函数注冊到 Nginx 中。

    -----------http/ngx_http_core_module.h:86-------------
typedef enum {
NGX_HTTP_POST_READ_PHASE = 0,
NGX_HTTP_SERVER_REWRITE_PHASE,
NGX_HTTP_FIND_CONFIG_PHASE,
NGX_HTTP_REWRITE_PHASE,
NGX_HTTP_POST_REWRITE_PHASE,
NGX_HTTP_PREACCESS_PHASE,
NGX_HTTP_ACCESS_PHASE,
NGX_HTTP_POST_ACCESS_PHASE,
NGX_HTTP_TRY_FILES_PHASE,
NGX_HTTP_CONTENT_PHASE,
NGX_HTTP_LOG_PHASE
} ngx_http_phases;

一个请求相应的 phase 会随着程序的运行发生变化。

同一时候,在请求的实际处理过程中。 会因等待事件或者子请求等导致请求在不同的 phase 重复处理,可是在随意时刻。对某个 指定的client请求而言。它总是处于某个确定的 phase 中。这里先临时将这个 phase 的转 化过程称为
phase machine。整个 phase machine 的退出点就是请求的全然处理结束。

处理函数注冊

全部的 phase 和在各 phase 注冊的入口函数。都暂时保存在 ngx_http_core_modulemain conf 中的配置结构体
ngx_http_core_main_conf_tphases 成员中:

    ----------http/ngx_http_core_module.h:125-------------
typedef struct {
ngx_array_t handlers;
} ngx_http_phase_t; typedef struct {
...
ngx_http_phase_engine_t phase_engine;
...
ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]
} ngx_http_core_main_conf_t;

ngx_http_rewrite_module 举例,其模块的 postconfiguration 相应回调函数
ngx_http_rewrite_init

    -----------http/modules/ngx_http_rewrite_module.c:104-----
static ngx_http_module_t ngx_http_rewrite_module_ctx = {
NULL, /* preconfiguration */
ngx_http_rewrite_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_rewrite_create_loc_conf, /* create location configuration */
ngx_http_rewrite_merge_loc_conf, /* merge location configuration */
};

ngx_http_rewrite_init 中,将 ngx_http_rewrite_module 的入口函数注冊到了
NGX_HTTP_SERVER_REWRITE_PHASENGX_HTTP_REWRITE_PHASE 两个 phase 中:

    -----------http/modules/ngx_http_rewrite_module.c:266-------
static ngx_int_t
ngx_http_rewrite_init(ngx_conf_t *cf)
{
...
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers;
...
*h = ngx_http_rewrite_handler; h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers;
...
*h = ngx_http_rewrite_handler;
...
}

然后,在全部 handler 模块的入口函数都注冊完成后,ngx_http_core_module 会将全部 入口函数封装成
ngx_http_phase_handler_t
类型变量后,线性化存储到 cmcf->phase_engine 中,同一时候在连续内存基础上。使用
next 成员再次生成一个链表 (通过数组索引链接)。

ngx_http_phase_handler_t 类型变量的
checker 成员函数,决 定一个请求在 phase machine 中。在当前 phase 的当前 handler 函数处理完后,是该 调用同一 phase 中的下一个 handler 函数处理,还是直接进行下一个 phase (checker 能够觉得是 phase 的调度函数)。

同一时候。在线性化过程中,还增加了 Nginx 内建 phase (find_config, post_rewrite,
post_access, try_files)的 checker 函数,这些 phase 原本身并不和不论什么模块的 handler 处理函数相相应 。

    ---------http/ngx_http_core_module.h:111--------------
sturct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker;
ngx_http_handler_pt handler;
ngx_uint_t next;
}; typedef struct {
ngx_http_phase_handler_t *handlers;
ngx_uint_t server_rewrite_index;
ngx_uint_t location_rewrite_index;
} ngx_http_phase_engine_t;

线性化处理由函数 ngx_http_init_phase_handlers 完毕,它在全部 http 模块的 postconfiguration 被调用完毕后运行:

    ---------http/ngx_http.c:328------
for (m = 0; ngx_modules[m]; m++) {
...
if (module->postconfiguration) {
if (module->postconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
...
if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}

ngx_http_init_phase_handlers 运行完后,各 phase 中注冊的回调函数被封装成 ngx_http_phase_handler_t 类型的变量,其字段的 checker 和 next 终于值总结例如以下:

    phase                   checker                             next
------------------------------------------------------------------------
POST_READ ngx_http_core_generic_phase 下一个 phase
SERVER_REWRITE ngx_http_core_generic_phase 下一个 phase
FIND_CONFIG ngx_http_core_find_config_phase 0
REWRITE ngx_http_core_generic_phase 下一个 phase
POST_REWRITE ngx_http_core_post_rewrite_phase FIND_CONFIG phase
PREACCESS ngx_http_core_generic_phase 下一个 phase
ACCESS ngx_http_core_access_phase 下下一个 phase (跳过 post_access phrase)
POST_ACCESS ngx_http_core_post_access_phase 下一个 phase
TRY_FILES ngx_http_core_try_files_phase 0
CONTENT ngx_http_core_content_phase NULL
------------------------------------------------------------------------

当中。各个 phase 的 checker 的行为例如以下:

    checker                             behavior
------------------------------------------------------------------------
ngx_http_core_generic_phase NGX_OK时,进入下一个 phase,
NGX_DECLINED时,下一个 handler
ngx_http_core_find_config_phase 下一个 handler
ngx_http_core_post_rewrite_phase 下一个 phase。即 FIND_CONFIG_PHASE
ngx_http_core_access_phase NGX_OK时,进入下一个 phase。
NGX_DECLINED, NGX_HTTP_FORBIDDEN,
NGX_HTTP_UNAUTHORIZED时。下一个 handler
ngx_http_core_post_access_phase r->access_code 不为0时。结束请求
否则。继续下一个 handler
ngx_http_core_try_files_phase 下一个 handler
ngx_http_core_content_phase 下一个 handler。或者结束请求
------------------------------------------------------------------------

另外一个须要注意的地方是。在 ngx_http_init_phase_handlers 中,仅仅给 cmcf->phase_engine.handlers 分配了多余的
sizeof(void *) 个字节用于存储 handlers 结束标识 NULL:

    ---------http/ngx_http.c:488---------------
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 *));

而在随后的使用中,仅仅检查了 ngx_http_phase_handler_t 的第一个字段,即 checker。 来推断是否到了
ngx_http_phase_handler_t 数组的末尾。这是有意为之,还是疏忽的地 方? 尽管代码能够正常执行,可是…

    --------http/ngx_http_core_module.c:836---------
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
...
while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
...
}
}

处理函数调用

在 Nginx 正常启动后,開始处理client请求。

在处理client请求的过程中。就会依据请求 处于的 phase 。调用已注冊到对应 phase 的回调函数,对请求进行处理。

当一个 HTTP 请求到达 Nginx 后。Nginx 会依次调用以下的处理函数 (先简单列出,等专 门再辟出一文对 HTTP 请求的“一生”进行具体描写叙述):

    - ngx_http_init_connection
- ngx_http_init_request
- ngx_http_process_request_line
- ngx_http_process_request_headers
- ngx_http_process_request
- ngx_http_handler
- ngx_http_core_run_phasess

最后一个函数 ngx_http_core_run_phases 就是这个 phase machine 的驱动函数:

    -----------http/ngx_http_core_module.c:836--------
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
...
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; while (ph[r->phase_handler].checker) {
rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
if (rc == NGX_OK) {
return;
}
}
}

随后,整个请求处理的主要逻辑都是在这个 phase machine 中完毕的。具体的处理逻辑 以后再分析,这篇关注的是 handler 模块的注冊和调用逻辑。

filter 模块

filter 模块的初始化时间和 handler 模块一样,都是在模块的 postconfiguration 回 调函数中完毕。可是,与 handler 模块函数的注冊方式不同的时,filter 模块的入口函数 不须要区分 phase。同一时候,全部的 filter 入口函数组成
output filter chain

每一个请 求的响应数据都经过 output filter chain 中的每一个 filter 入口函数进行处理,然后 才会发向网络。

响应数据由响应头和响应体两部分组成,分别经过响应头相应的 header output filter chain 和响应体相应的 body output filter chain 处理。

处理函数注冊

output filter chain。使用两个全局变量 ngx_http_top_header
ngx_http_top_body_filter
分别保存第一个 header filter 和 body filter 入口函数 的地址。

每一个 filter 模块的局部变量
ngx_http_next_header_filterngx_http_next_body_filter 保存当前 filter 模块运行完后须要调用的下一个 filter 模块的入口函数地址。这个 filter 链条在 Nginx初始化模块时完毕。 比方ngx_http_addition_filter 模块的初始化函数例如以下:

    static ngx_int_t
ngx_http_addition_filter_init(ngx_conf_t *cf)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_addition_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_addition_body_filter; return NGX_OK;
}

header output filter chain 为例,Nginx在開始返回响应数据时,调用 ngx_http_top_header_filter
ngx_http_top_header_filter 指向的第一个 filter 模块的入口函数,第一个 filter 模块的入口函数处理完毕以后。会调用其模块局部变量
ngx_http_next_header_filterngx_http_next_header_filter 指向的下一个 filter 模块的入口函数,以此类推,直到
ngx_http_header_filterngx_http_write_filter 模块将数据发回client。这个处理过程就完毕了。

filter 模块入口函数的调用顺序由 filter 模块的初始化顺序决定,而且方向相反。模块 初始化顺序在 objs/ngx_modules.c 中定义:

     34 extern ngx_module_t  ngx_http_write_filter_module;
35 extern ngx_module_t ngx_http_header_filter_module;
36 extern ngx_module_t ngx_http_chunked_filter_module;
37 extern ngx_module_t ngx_http_range_header_filter_module;
38 extern ngx_module_t ngx_http_gzip_filter_module;
39 extern ngx_module_t ngx_http_postpone_filter_module;
40 extern ngx_module_t ngx_http_charset_filter_module;
41 extern ngx_module_t ngx_http_ssi_filter_module;
42 extern ngx_module_t ngx_http_userid_filter_module;
43 extern ngx_module_t ngx_http_headers_filter_module;
44 extern ngx_module_t ngx_http_copy_filter_module;
45 extern ngx_module_t ngx_http_range_body_filter_module;
46 extern ngx_module_t ngx_http_not_modified_filter_module;

可见,ngx_http_header_filter_modulengx_http_write_filter_module 最先被初 始化。那么,其提供的filter处理函数在整个响应处理过程中被最后调用。

body output filter chain 的生成方式和 header output filter chain 类似。

处理函数调用

在请求的响应内容准备完成,開始向client发送响应数据时。Nginx 会使用 ngx_http_send_header将响应包头数据交给
header output filter chain 处理。

包头 数据发送完毕后,再使用 ngx_http_output_filter 将响应包体数据交给
body output filter chain 处理。

    -----------http/ngx_http_core_module.c:1702-----------
ngx_int_t
ngx_http_send_header(ngx_http_request_t *r)
{
if (r->err_status) {
r->headers_out.status = r->err_status;
r->headers_out.status_line.len = 0;
} return ngx_http_top_header_filter(r);
} ----------http/ngx_http_core_module.c:1713-------------
ngx_int_t
ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
...
rc = ngx_http_top_body_filter(r, in);
}

随后的处理流程。就是依照 处理函数注冊 中构造的 output filter chain 的顺序调用 各个 filter 模块的入口函数一步一步完毕的。

详细的逻辑,就和各个 filter 模块本身的 功能相关了。

nginx源代码分析--模块分类的更多相关文章

  1. 新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

    新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  2. 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t

    nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

  3. Nginx源代码分析—业务流程

    Nginx源代码分析-业务流程 到此为止,我们如果ngx_init_cycle已经结束.我们临时无论他做了什么,我们从他做的效果进入. 从常理上来讲,假设一个请求到达,那么我们须要接受这个请求,那么就 ...

  4. nginx源代码分析--进程间通信机制 &amp; 同步机制

    Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...

  5. nginx源代码分析--配置文件解析

    ngx-conf-parsing 对 Nginx 配置文件的一些认识: 配置指令具有作用域,分为全局作用域和使用 {} 创建其他作用域. 同一作用域的不同的配置指令没有先后顺序:同一作用域能否使用同样 ...

  6. nginx源代码分析之内存池实现原理

    建议看本文档时结合nginx源代码. 1.1   什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理.应用程序申请内存时不再与OS打交道.而是从内存池中申请内存或者释放内存到内存池 ...

  7. nginx源代码分析--从源代码看nginx框架总结

    nginx源代码总结: 1)代码中没有特别绕特别别扭的编码实现.从变量的定义调用函数的实现封装,都非常恰当.比方从函数命名或者变量命名就能够看出来定义的大体意义,函数的基本功能,再好的架构实如今编码习 ...

  8. nginx中的模块分类及常见核心模块有哪些

    1.模块分类 核心模块:是 Nginx 服务器正常运行必不可少的模块,提供错误日志记录 .配置文件解析 .事件驱动机制 .进程管理等核心功能 标准HTTP模块:提供 HTTP 协议解析相关的功能,比如 ...

  9. nginx源代码分析--event事件驱动初始化

    1.在nginx.c中设置每一个核心模块的index ngx_max_module = 0; for (i = 0; ngx_modules[i]; i++) { ngx_modules[i]-> ...

随机推荐

  1. 基于JSP+SERVLET的新闻发布系统(三)

    拖了这么久..今天把栏目管理还有新闻管理模块的也挂出来.. 栏目管理跟用户管理一样. 这里重点讲解新闻管理. 效果图如上: 1,可选择栏目类别,且栏目类别是动态生成的. 默认生成的文章是未审核状态的. ...

  2. signal()函数说明

    表头文件#include<signal.h> 功 能:设置某一信号的对应动作 函数原型:void (*signal(int signum,void(* handler)(int)))(in ...

  3. PHP - mysql使用参数数据

    "SELECT dg_id FROM dg_user WHERE dg_username = '{$clean['username']}' LIMIT 1","该用户已经 ...

  4. 基于visual Studio2013解决算法导论之003雇佣问题

     题目 雇用问题 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <malloc.h> #in ...

  5. MinGW介绍与使用

    3.1:MinGW 是什么? MinGW 提供了一套简单方便的Windows下的基于GCC 程序开发环境.MinGW 收集了一系列免费的Windows 使用的头文件和库文件:同时整合了GNU ( ht ...

  6. Oracle undo 镜像数据探究

                                                                 Oracle undo 镜像数据探究  今天是2013-08-18,隔别一周的 ...

  7. 用定时器T0查询方式P0口8位控制LED闪烁

    #include<reg52.h> #define uchar unsigned char #define uint unsigned int void main (void) { uch ...

  8. 有关android工程的构建脚本(build.xml)的学习

    学习[android-sdk-linux根目录]/tools/ant/build.xml,觉得如下几点很有用,记录之 1)ant脚本中属性值是于前置定义优化的原则,即属性发生重复定义时,前面定义的值不 ...

  9. Microsoft Win32 Programmer's Reference.chm

    实在是太棒了,感谢这位网友: http://download.csdn.net/detail/tgyd6800/9632351

  10. Cppcheck 1.54 C/C++静态代码分析工具

    Cppcheck是一个C/C++代码分析工具,只检测那些编译器通常无法检测到的bug类型.   官方上建议让编译器提供尽量多的警告提示:1.使用Visual C++的话,应使用警告等级4 2.使用GC ...