nginx的模块很之多。能够觉得全部代码都是以模块的形式组织。这包含核心模块和功能模块,针对不同的应用场合。并不是全部的功能模块都要被用到,附录A给出的是默认configure(即简单的httpserver应用)下被连接的模块,这里虽说是模块连接。但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序运行时再进行动态载入。nginx模块源文件会在生成nginx时就直接被编译到其二进制运行文件里。所以假设要选用不同的功能模块。必须对nginx做又一次配置和编译。对于功能模块的选择。假设要改动默认值,须要在进行configure时进行指定,比方新增http_flv功能模块(默认是没有这个功能的,各个选项的默认值能够在文件auto/options内看到)

[root@localhost nginx-1.2.0]# ./configure --with-http_flv_module

运行后。生成的objs/ngx_modules.c文件内就包括有对ngx_http_flv_module模块的引用了,要再去掉http_flv功能模块。则须要又一次configure,即不带--with-http_flv_module配置后再编译生成新的nginx运行程序。通过运行./configure –help。我们能够看到很多其它的配置选项。

尽管nginx的模块的非常多。而且某个模块的功能各不同样,可是能够依据功能特性,我们大致能够分为四类:

1) handlers : 处理client请求并产生响应内容,比方ngx_http_static_moudle模块。负责client的静态页面请求。并将相应的静态磁盘文件作为响应内容输出.

2)filters : 对handlers产生的响应内容做各种过滤处理(即增。删,改),比方 ngx_http_not_modify_filter_moudle,假设通过时间推断前后2次请求的响应内容没有发生不论什么改变,那么能够直接响应"304 Not Modified"状态标识,让client使用缓存就可以,而原本发送的响应内容将被清除掉.

3)upstream : 假设存在后端真实的server,nginx 能够利用upstream模块充当反向代理的角色,对client的请求仅仅负责转发到后端的真实server,如ngx_http_proxy_moudle模块.

4)load-balance : 在nginx充其中间代理时,因为后端真实server往往多于一个,对于某一次client的请求,怎样选择相应的后端真实server来进行处理。这就有类似于ngx_http_upstream_ip_hash_module这种模块来实现不同的负载均衡算法(Load Balance)。

在此,我们先来了解一些数据结构:

struct ngx_module_s {
ngx_uint_t ctx_index; //在同类模块中的序号
ngx_uint_t index; //在全部模块中序号 ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3; ngx_uint_t version; //当前模块的版本 void *ctx; //指向当前模块特有的数据
ngx_command_t *commands; //指向当前模块配置项解析数组
ngx_uint_t type; //模块的类型
//回调函数
ngx_int_t (*init_master)(ngx_log_t *log); ngx_int_t (*init_module)(ngx_cycle_t *cycle); ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle); void (*exit_master)(ngx_cycle_t *cycle);
//保留字
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};

结构体ngx_module_s中值得注意的几个字段 ctx , commands, type,当中commands字段表示当前模块的能够解析的配置项目。表示模块类型的type值仅仅有5种,而同一类型的模块的ctx数据类型都是同样的。

序号

type值

ctx指向数据类型

1

NGX_CORE_MODULE

ngx_core_module_t

2

NGX_EVENT_MODULE

ngx_event_module_t

3

NGX_CONF_MODULE

NULL

4

NGX_HTTP_MODULE

ngx_http_module_t

5

NGX_MAIL_MODULE

ngx_mail_module_t

typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
............................
typedef struct {
ngx_str_t *name; void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf); ngx_event_actions_t actions;
} ngx_event_module_t;
........................ typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf); void *(*create_main_conf)(ngx_conf_t *cf);
char *(*init_main_conf)(ngx_conf_t *cf, void *conf); void *(*create_srv_conf)(ngx_conf_t *cf);
char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); void *(*create_loc_conf)(ngx_conf_t *cf);
char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

上面表中的第3列的数据类型比較重要。它的字段基本上都是一些回调函数,这些回调函数会在其模块相应的配置文件解析过程 前/中/后 会适时被调用,做一些内存准备。初始化,配置值检查,配置值填充。合并,回调函数挂载等初始工作.

以下我们以ngx_http_core_moudle模块为例,type 为 NGX_HTTP_MOUDLE, ctx 指向ngx_http_moudle_t结构体变量ngx_http_core_module_ctx.

static ngx_http_module_t  ngx_http_core_module_ctx = {
ngx_http_core_preconfiguration, /* preconfiguration */
NULL, /* postconfiguration */ ngx_http_core_create_main_conf, /* create main configuration */
ngx_http_core_init_main_conf, /* init main configuration */ ngx_http_core_create_srv_conf, /* create server configuration */
ngx_http_core_merge_srv_conf, /* merge server configuration */ ngx_http_core_create_loc_conf, /* create location configuration */
ngx_http_core_merge_loc_conf /* merge location configuration */
};

依据上面的代码。我们能够非常明显看到各个回调函数的回调时机,比如:ngx_http_core_preconfiguration将在进行http块配置解析前被调用。所以内在ngx_http_block()函数里看到这种代码:

static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
} module = ngx_modules[m]->ctx; if (module->preconfiguration) {
if (module->preconfiguration(cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
}
}

至于这些回调函数内的详细逻辑,如前所述通常是一些初始或默认值填充工作,但也有回调函数挂载的设置。比方ngx_http_static_module模块的postconfiguration字段回调函数ngx_http_static_init()就是将自己的处理函数ngx_http_static_handler()挂载在http处理状态机上,但整体来看这毕竟都仅仅是一些简单的初始准备工作.

handlers模块

对于clienthttp请求过程。为了获得更强的控制力,nginx将其细分成多个阶段处理,每一个阶段都有零个或多个回调函数专门处理,当我们编写handler模块的时候,必须把模块功能挂载在正确的阶段点。如前面所描写叙述的ngx_http_static_moudle 将自己的功能模块处理函数ngx_http_static_handler()挂载在NGX_HTTP_CONTENT_PHASE阶段.

Http请求处理过程一共分为11个阶段,每个阶段相应的处理功能都比較单一,这样能尽量让nginx模块代码更为内聚:

序号

阶段宏名

阶段描写叙述

0

NGX_HTTP_POST_READ_PHASE

读取请求内容阶段

1

NGX_HTTP_SERVER_REWRITE_PHASE

Server请求地址重写阶段

2

NGX_HTTP_FIND_CONFIG_PHASE

配置查找阶段

3

NGX_HTTP_REWRITE_PHASE

Location请求地址重写阶段

4

NGX_HTTP_POST_REWRITE_PHASE

请求地址重写提交阶段

5

NGX_HTTP_PREACCESS_PHASE

訪问权限检查准备阶段

6

NGX_HTTP_ACCESS_PHASE

訪问权限检查阶段

7

NGX_HTTP_POST_ACCESS_PHASE

訪问权限检查提交阶段

8

NGX_HTTP_TRY_FILES_PHASE

配置项try_files处理阶段

9

NGX_HTTP_CONTENT_PHASE

内容产生阶段

10

NGX_HTTP_LOG_PHASE

日志模块处理阶段

并不是某个阶段都能挂载自己定义的回调函数,比方NGX_HTTP_TRY_FILE_PHASE阶段就是针对配置项try_files的特定处理阶段段。NGX_HTTP_FIND_CONFIG_PHASE、NGX_HTTP_POST_ACCESS_PHASE与NGX_HTTP_POST_REWRITE_PHASE这三个阶段也是为了完毕nginx特定的功能,就算给这几个阶段加上回调函数。也永远不会被调用。我们的自己定义模块回调函数挂载在NGX_HTTP_CONTENT_PHASE阶段的情况比較多,毕竟大部分情况下的业务需求是改动HTTP响应数据。nginx自身的产生响应内容的模块。像ngx_http_static_module、ngx_http_random_index_module、ngx_http_index_module、ngx_http_gzip_static_module、ngx_http_dav_module等都是挂载在这个阶段。

大多数情况下,功能模块会在其相应配置解析完后的回调函数,也就是ngx_http_moudle_t结构体的postconfiguration字段指向的函数内将当前模块的回调功能函数挂载到这11个阶段当中一个上.

以ngx_http_static_module为例:

ngx_http_module_t  ngx_http_static_module_ctx = {
NULL, /* preconfiguration */
ngx_http_static_init, /* postconfiguration */ NULL, /* create main configuration */
NULL, /* init main configuration */ NULL, /* create server configuration */
NULL, /* merge server configuration */ NULL, /* create location configuration */
NULL /* merge location configuration */
}; 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); 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;

在模块ngx_http_static_module的postconfiguration回调函数ngx_http_static_init()内。将ngx_http_static_module模块的核心功能函数ngx_http_static_handler()挂载在Http请求处理流程中的NGX_HTTP_CONTENT_PHASE阶段。

这样。当一个client的http静态页面请求发送到nginxserver,nginx就行调用到我们这里注冊的ngx_http_static_handler()函数,

各个功能模块将其自身的功能函数挂载在cmcf->phases后,内部的情况例如以下图所看到的:

回调函数会依据模块的不同而不同.这些回调函数的调用都时有条件的,调用后也要做一些依据返回值的结果处理.比方某次处理是否进入到阶段NGX_HTTP_CONTENT_PARSE的回调函数的处理,这须要一个事前推断.所以在函数ngx_http_init_phase_handlers()里对全部这个回调函数进行一次重组.

struct ngx_http_phase_handler_s {
ngx_http_phase_handler_pt checker; //阶段检查函数
ngx_http_handler_pt handler;
ngx_uint_t next;
};

但从上图中能够看到,该函数仅仅把有回调函数的处理阶段给提取了出来,同一时候利用ngx_http_phase_handler_t结构体数组对这些回调函数进行重组,不仅加上了进入回调函数的条件推断checker函数,并且通过next字段的使用,把原本的二维数组实现转化为可直接在一维函数数组内部跳动;一般来讲,二维数组的遍历须要两层循环。而遍历一维函数数组就仅仅需一层循环。

再来看对http请求进行分段处理的核心函数ngx_http_core_run_phase:

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; while (ph[r->phase_handler].checker) { rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); if (rc == NGX_OK) {
return;
}
}
}
ngx_http_core_run_phases函数中r->phase_handler标志当前处理的序号。对于一个client的最開始的请求的时刻。 该值当然就是0了,while循环推断假设存在checker函数(末尾数组元素的checker函数为null),那就调用该checker函数并有可能调用对应的回调函数,以NGX_HTTP_ACCESS_PHASE阶段的ngx_http_core_access_phase()函数为例:
ngx_int_t
ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
if (r != r->main) {
r->phase_handler = ph->next;
return NGX_AGAIN;
} rc = ph->handler(r); if (rc == NGX_DECLINED) {
r->phase_handler++;
return NGX_AGAIN;
} if (rc == NGX_AGAIN || rc == NGX_DONE) {
return NGX_OK;
}
ngx_http_finalize_request(r, rc);
return NGX_OK;
}

能够看到,一个功能模块的handler函数能够返回多种类型的值,而且这些值有其固有的含义:

序号

返回值

含义

1

NGX_OK

当前阶段已经被成功处理,必须进入到下一个阶段

2

NGX_DECLINED

当前回调不处理当前情况,进入到下一个回调处理

3

NGX_AGAIN

当前处理所需资源不足,须要等待所依赖事件发生

4

NGX_DONE

当前处理结束,仍需等待进一步事件发生后做处理

5

NGX_ERROR, NGX_HTTP_…

当前回调处理错误发生,须要进入到异常处理流程


Filter模块:
对于http请求处理handlers产生的响应内容,在输出client之前须要做过滤处理,这些过滤处理对于完整功能的增强实现和性能的提升是很有必要的,比方过滤模块ngx_http_chunked_filter_moudle,那么就无法完整支持http中chunk的功能。 假设没有ngx_http_not_modified_filter_module过滤模块。那么就无法让client使用本地缓存来提高性能;诸如这些都须要过滤模块的支持。因为响应数据包含响应头和响应体,所以以此相应,任一filter模块必须提供处理响应头的header过滤函数(比方ngx_http_not_modified_filter_module模块提供的ngx_http_not_modified_header_filter()函数)或处理响应体的body过滤功能函数(比方ngx_http_copy_filter_module模块提供的ngx_http_copy_filter()函数)或两者皆有(比方ngx_http_chunked_filter_module模块提供的ngx_http_chunked_header_filter()函数和ngx_http_chunked_body_filter()函数)。

全部的header过滤功能函数和body过滤功能函数会分别组成各自的两条过滤链。例如以下图所看到的(使用附录A所列模块):

这2条过滤链怎么形成的呢?在源文件ngx_http.c中,能够看到有2个函数指针变量:

ngx_int_t  (*ngx_http_top_header_filter) (ngx_http_request_t *r);

ngx_int_t  (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch);

这是整个nginx范围内可见的全局变量。然后在每个filter模块内,我们还会看到类似于这种定义(假设当前模块仅仅有header过滤功能函数或仅仅有body过滤功能函数。那么例如以下定义也就仅仅有对应的那个变量):

static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

注意到static修饰符,也就是说这两个变量是属于模块范围内可见的局部变量。

有了这些函数指针变量,再在各个filter模块的postconfiguration回调函数(该函数会在其相应配置解析完后被调用做一些设置工作,前面已经描写叙述过)内。全局变量与局部变量的巧妙赋值使得终于行成了两条过滤链。以header过滤链为例,通过附录A的模块列表ngx_modules变量。能够看到ngx_http_header_filter_module是具有header过滤功能函数的序号最小的过滤模块,其postconfiguration回调函数例如以下:

618:    ngx_http_header_filter_init(ngx_conf_t *cf)
619: {
620: ngx_http_top_header_filter = ngx_http_header_filter;
621:
622: return NGX_OK;
623: }
232:    static ngx_int_t
233: ngx_http_chunked_filter_init(ngx_conf_t *cf)
234: {
235: ngx_http_next_header_filter = ngx_http_top_header_filter;
236: ngx_http_top_header_filter = ngx_http_chunked_header_filter;
}

其他过滤模块的类此增加,逐步形成终于的完整header过滤链;当然。body过滤链的形成过程也与此类似。两条过滤链形成后。其相应的调用入口分别在函数ngx_http_send_header()和函数ngx_http_output_filter()内:

1889:    ngx_int_t
1890: ngx_http_send_header(ngx_http_request_t *r)
1891: {
1892: …
1897: return ngx_http_top_header_filter(r);
1898: }
1899:
1901: ngx_int_t
1902: ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
1903: {
1904: …
1912: rc = ngx_http_top_body_filter(r, in);
1913: …
1919: return rc;
1920: }

这两个函数很easy。主要是通过过滤链的链头函数指针全局变量进入到两条过滤链内,进而依次运行链上的各个函数。

比方这里ngx_http_top_header_filter指向的是ngx_http_not_modified_header_filter()函数,因此进入到该函数内运行。而在该函数的运行过程中又会依据情况。继续通过当前模块内的函数指针局部变量ngx_http_next_header_filter间接的调用到header过滤链的下一个过滤函数,这对保证过滤链的前后承接是很必要的。除非我们遇到无法继续处理的错误。此时仅仅有返回NGX_ERROR这种值:

52:    static ngx_int_t
53: ngx_http_not_modified_header_filter(ngx_http_request_t *r)
54: {
55: …
70: return ngx_http_next_header_filter(r);
71: }

依据HTTP协议具备的响应头影响或决定响应体内容的特点,所以通常是先对响应头进行过滤,依据头过滤处理返回值再对响应体进行过滤处理。假设在响应头过滤处理中出错或某些特定情况下,响应体过滤处理能够不用再进行。

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

  1. nginx源代码分析--事件模块 & 琐碎

    通过HUP信息使得NGINX实现又一次读取配置文件,使用USR2信号使得NGINX实现平滑升级. 在nginx中有模块这么一说,对外全部的模块都是ngx_module_t类型,这个结构体作为全部模块的 ...

  2. nginx源代码分析--nginx进程间通信

    Linux下的IPC非常多,nginx的进程都是有亲缘关系的进程,对于他们的通信我们选择TCP socket进行通信.   TCP socket 用来做进程通信的优点有,   1.socket是文件描 ...

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

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

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

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

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

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

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

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

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

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

  8. nginx源代码分析--模块分类

    ngx-modules Nginx 基本的模块大致能够分为四类: handler – 协同完毕client请求的处理.产生响应数据.比方模块, ngx_http_rewrite_module, ngx ...

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

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

随机推荐

  1. CentOS 6.9下的Setup工具(用于管理服务/防火墙/网络配置/验证服务)

    说明:Setup工具套件好像是CentOS下特有的用于管理服务/防火墙/网络配置等,其实就是基于命令行模式界面的GUI工具.唯一特点就是方便. 安装: #安装Setup命令工具 yum -y inst ...

  2. extjs4权限管理,actioncolumn列显示隐藏或禁用

    //隐藏getClass: function(v, meta, rec) { if ( someCondition ) { return 'x-hidden' } }//加入其他样式getClass: ...

  3. Storm如何保证消息不丢失

    storm保证从spout发出的每个tuple都会被完全处理.这篇文章介绍storm是怎么做到这个保证的,以及我们使用者怎么做才能充分利用storm的可靠性特点. 一个tuple被"完全处理 ...

  4. [Functional Programming] Randomly Pull an Item from an Array with the State ADT (Pair)

    Functor composition is a powerful concept that arises when we have one Functor nested in another Fun ...

  5. 让Qt Creator支持Windows Phone 8开发

    让Qt Creator支持Windows Phone 8开发 近期QtCreator3.2出了.修复了一些Bug.比上一个版本号3.1.2要好了一些. 因为在上一个版本号(Qt for WinRT自带 ...

  6. hdu 5265 技巧题 O(nlogn)求n个数中两数相加取模的最大值

    pog loves szh II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  7. CSS--百度百科

    层叠样式表是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言. CSS目前最新版本为CSS3,是能够真正做到网页表现与内容分离的一种样式设 ...

  8. 微信小程序 解决 数字粗细不一 的bug

    1.bug 2.原因解析 微信小程序本身字体问题 3.解决方案 设置字体 font-family: Microsoft YaHei; .

  9. JavaScript对象深复制

    1.原理 使用JSON,当然需要JSON安全的格式,JSON安全请参考:http://www.cnblogs.com/mengfangui/p/8257269.html 2.示例 <!DOCTY ...

  10. 基于windows api实现的共享锁/独占锁

    众所周知,windows平台上实现线程同步.或者说资源的加锁与解锁的方法有内核事件.临界区.相互排斥量.信号量,甚至interlocked系列函数等多种手段. 可是在日常的编程中,我们使用这些手段对  ...