nginx:模块的理解以及划分

缘由

看了深入理解nginx 第八章 nginx基础架构之后,对整个nginx的模块还是有了比较清晰的认识。所以在这里总结一下。
首先要明确的就是:模块化设计是面向过程设计中常用的一种手段。太抽象我现在的级别还是领悟不了,我看了一下百度百科,随便摘两句话:
  • 模块化是指解决一个复杂问题时自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。
  • 模块化用来分割,组织和打包软件。每个模块完成一个特定的子功能,所有的模块按某种方法组装起来,成为一个整体,完成整个系统所要求的功能。

有哪些模块呢

要知道整个nginx有哪些模块,关键就是知道在nginx里面是如何定义一个模块的。那就是
  • ngx_module_t
这个结构体,所以,只要是一个模块需要使用ngx_module_t对其对应。我觉得我可以使用这个作为关键字在整个文档中作一个搜索,那么我就知道到底一共有哪些模块了。如下图所示:

我们已经看到一些模块的结构体了。比如,ngx_epoll_module模块,这就是事件模块中使用epoll机制的模块。我昨天还翻译相关linux手册中的对epoll的解释。

当然,很多模块都是我们不常用的,那么如果我想要知道有哪些模块是常用的呢?
我建议去看官网看看手册:
  • http://nginx.org/en/docs/
在页面最下面的Modules reference部分,就是对一些模块的解释和配置方式的讲解,所以我认为其应该是在nginx这个服务器上比较常用的模块。当然,这是从配置的角度,而不是从源码的角度。

另外一个问题就是在这次运行的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的取值主要是:
  1. NGX_CORE_MODULE
  2. NGX_HTTP_MODULE
  3. NGX_CONF_MODULE
  4. NGX_EVENT_MODULE
  5. NGX_MAIL_MODULE
这就意味着模块的划分。当然,具体某一个小模块是干什么的,恐怕还需要具体的分析。

nginx的模块划分

书中已经把nginx的模块划分讲述的非常清楚了。我们来看看下面这张图:

很清楚,我看到一共分为了五个模块,而每个大模块下面包含了很多子模块,可以看出他们都是以功能为单位而集合在一起的。含义也非常明确。比如核心模块,那么必然是整个nginx的核心所在。而配置模块,显然是为了解析配置文件而存在的。核心模块与其他功能模块都具有着关系。

从图中可以看出,核心模块内都有一个子模块是与另一个功能模块相关的,比如,HTTP模块和核心模块中的ngx_http_module是相关的。ngx_http_module作为核心模块的一部分,完成的主要功能是:ngx_http_module完成的功能主要在核心模块内定义http模块,同样,ngx_events_module模块也是在核心模块内定义整个events模块。
此外,在每一个功能模块内又有一个小的核心模块,名字中都带有core,就是上图中,黑框部分,比如
  1. mail功能模块的核心模块:ngx_mail_core_module
  2. http功能模块的核心模块:ngx_http_core_module
  3. event功能模块的核心模块:ngx_event_core_module
这三个小的核心模块完成相对应的所有的功能模块的加载。比如ngx_http_core_module就是负责去加载ngx_http_up_stream_module。而ngx_event_core_module就要负责加载ngx_epoll_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也会在
  1. ngx_master_process_cycle(cycle);
  2. 或者
  3. ngx_single_process_cycle(cycle);
里面调用。
具体是上面哪个函数,取决于nginx被启动的方式,如何ngnix是在single模式下启动,也就是master和worker进程是同一个的情况下,那么就是ngx_single_process_cycle(cycle);

对于:exit_process和exit_master函数调用的地方,书上也有图,如下两幅:



目前看是来有一点点复杂,我们每一次都知道图中每一框的来源。那么慢慢对nginx就和无比熟悉。

总结

想了想,大概就说怎么多吧。这只是一份自己学习的总结。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值