缘由
看了深入理解nginx 第八章 nginx基础架构之后,对整个nginx的模块还是有了比较清晰的认识。所以在这里总结一下。首先要明确的就是:模块化设计是面向过程设计中常用的一种手段。太抽象我现在的级别还是领悟不了,我看了一下百度百科,随便摘两句话:
- 模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。
- 模块化用来分割,组织和打包软件。每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。
有哪些模块呢
要知道整个nginx有哪些模块,关键就是知道在nginx里面是如何定义一个模块的。那就是- ngx_module_t
我们已经看到一些模块的结构体了。比如,ngx_epoll_module模块,这就是事件模块中使用epoll机制的模块。我昨天还翻译相关linux手册中的对epoll的解释。
当然,很多模块都是我们不常用的,那么如果我想要知道有哪些模块是常用的呢?
我建议去看官网看看手册:
- http://nginx.org/en/docs/
另外一个问题就是在这次运行的nginx中有哪些模块被加载了呢?
在之前的学习中,我们知道,在编译之后,可以查阅
~/Documents/nginx-1.2.9/objs/ngx_modules.c
得知在此次nginx的二进制文件中有哪些文件被加载了。如果你自己添加了新的模块,那么在这里面也可以看见。
ngx_module_t
知道了如何查看有哪些模块,下一个问题就是如何知道哪些模块,有哪些功能了。所以这里非说说ngx_module_t不可。注意只要是nginx里面的一个模块,就肯定会使用ngx_module_t结构体来表明其作为模块的一些信息。#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
/**
* 下面6个字段在定义模块的时候用
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
来赋值即可,里面有用的主要是ctx_index和index。
*/
ngx_uint_t ctx_index; //代表一类模块中该模块的序号,可以认为是数组下标
ngx_uint_t index; //代表所有模块的下标没,在nginx.c文件的main函数内赋值
/**
* 保留字段
*/
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
ngx_uint_t version;
void *ctx; //指明一类模块的上下文,与下面的type字段是相对应的。
//不过类型的模块的功能相差很大,所以需要针对不同类型的模块
//指定不同的上下文。
ngx_command_t *commands;//使用这个结构体来处理与这个模块相关的配置项
ngx_uint_t type;//指明该模块的类型,取值只有5种
/**
* 下面六个是ngninx框架内会调用的各个模块的方法
*/
ngx_int_t (*init_master)(ngx_log_t *log);//暂时未使用
ngx_int_t (*init_module)(ngx_cycle_t *cycle);//启动worker进程前调用
ngx_int_t (*init_process)(ngx_cycle_t *cycle);//正常服务前,worker进程调用
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);//暂时未使用
void (*exit_thread)(ngx_cycle_t *cycle);//暂时未使用
void (*exit_process)(ngx_cycle_t *cycle);//停止服务前worker进程退出时调用
void (*exit_master)(ngx_cycle_t *cycle);//master进程推出前调用
/* 下面几个都是保留字段,无用。使用
* #define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0
* 来填充即可。
*/
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;
};
其中type字段,就告诉了我们这个模块大致要干什么,干的事件和nignx作为web服务器的哪一个部分相关。type的取值主要是:
- NGX_CORE_MODULE
- NGX_HTTP_MODULE
- NGX_CONF_MODULE
- NGX_EVENT_MODULE
- NGX_MAIL_MODULE
nginx的模块划分
书中已经把nginx的模块划分讲述的非常清楚了。我们来看看下面这张图:很清楚,我看到一共分为了五个模块,而每个大模块下面包含了很多子模块,可以看出他们都是以功能为单位而集合在一起的。含义也非常明确。比如核心模块,那么必然是整个nginx的核心所在。而配置模块,显然是为了解析配置文件而存在的。核心模块与其他功能模块都具有着关系。
从图中可以看出,核心模块内都有一个子模块是与另一个功能模块相关的,比如,HTTP模块和核心模块中的ngx_http_module是相关的。ngx_http_module作为核心模块的一部分,完成的主要功能是:ngx_http_module完成的功能主要在核心模块内定义http模块,同样,ngx_events_module模块也是在核心模块内定义整个events模块。此外,在每一个功能模块内又有一个小的核心模块,名字中都带有core,就是上图中,黑框部分,比如
- mail功能模块的核心模块:ngx_mail_core_module
- http功能模块的核心模块:ngx_http_core_module
- event功能模块的核心模块:ngx_event_core_module
一类模块的上下文
看ngx_module_s结构体内:struct ngx_module_s {
void *ctx; //指明一类模块的上下文,与下面的type字段是相对应的。
//不过类型的模块的功能相差很大,所以需要针对不同类型的模块
//指定不同的上下文。
ngx_command_t *commands;//使用这个结构体来处理与这个模块相关的配置项
ngx_uint_t type;//指明该模块的类型,取值只有5种
};
就像注释中所说的那样,一类模块有自己单独的上下文。如下图所示,这张图片展现了针对不同类型的模块,其ctx应该是怎样一个结构体。
ngx_module_t中的回调函数
struct ngx_module_s {
/**
* 下面六个是ngninx框架内会调用的各个模块的方法
*/
ngx_int_t (*init_master)(ngx_log_t *log);//暂时未使用
ngx_int_t (*init_module)(ngx_cycle_t *cycle);//启动worker进程前调用
ngx_int_t (*init_process)(ngx_cycle_t *cycle);//正常服务前,worker进程调用
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);//暂时未使用
void (*exit_thread)(ngx_cycle_t *cycle);//暂时未使用
void (*exit_process)(ngx_cycle_t *cycle);//停止服务前worker进程退出时调用
void (*exit_master)(ngx_cycle_t *cycle);//master进程推出前调用
};
光这样看,很难知道这几个函数到底在什么时候被调用了,作者用了很棒的流程图来表示。
只看init_module的方法:
从代码的角度来看,代码如下:
ngx_cycle_t *
ngx_init_cycle(ngx_cycle_t *old_cycle)
{
...
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->init_module) {
if (ngx_modules[i]->init_module(cycle) != NGX_OK) {
/* fatal */
exit(1);
}
}
}
...
}
正常情况下,ngx_init_cycle函数是被main函数调用的。那么还有一点值得注意,那就是在以不关闭nginx的方式重读配置文件的时候,那么ngx_init_cycle也会在
- ngx_master_process_cycle(cycle);
- 或者
- ngx_single_process_cycle(cycle);
具体是上面哪个函数,取决于nginx被启动的方式,如何ngnix是在single模式下启动,也就是master和worker进程是同一个的情况下,那么就是ngx_single_process_cycle(cycle);
对于:exit_process和exit_master函数调用的地方,书上也有图,如下两幅:
目前看是来有一点点复杂,我们每一次都知道图中每一框的来源。那么慢慢对nginx就和无比熟悉。