nginx从0到1之模块梳理

目标

      给大家看nginx源码时的一个简单引导。该文章围绕nginx模块展开,只是让大家对模块框架有个大体了解,并不涉及源码分析过程。

1, 高度模块化的设计是nginx的架构基础,在nginx中,除了少量的核心代码,其他一切皆为模块。

       高度抽象的模块接口,所有的模块都遵循同样的ngx_module_t接口设计规范,他也是所有模块的通用接口。

        在看ngx_module_t 结构体时重点关注以下四个成员

*ctx指针, type成员,ctx_index成员,index成员


        1)*ctx指针,它可以指向任何数据,也就可以指向任何类型的模块。

       对于HTTP类型的模块来说,ngx_module_t中的ctx指针必须指向ngx_http_module_t(ngx_http_config.h)接口,例如:ngx_http_core_module.c、ngx_http_header_filter_module.c、ngx_http_realip_module.c等。

        对于EVENT类型的模块来说,ngx_module_t中的ctx指针必须指向ngx_event_module_t接口,例如:ngx_epoll_module.c、ngx_poll_module.c、ngx_select_module.c等。

        对于MAIL类型的模块来说,ngx_module_t中的ctx指针必须指向ngx_mail_module_t接口,例如:ngx_mail_auth_http_module.c、ngx_mail_core_module.c,ngx_mail_pop3_module.c等。

        对于核心模块,ngx_module_t中ctx指针指向ngx_core_module_t接口,例如:nginx.c(ngx_core_module)、ngx_log.c(ngx_errlog_module)、ngx_event.c (ngx_events_module)、ngx_event_openssl.c(ngx_openssl_module)、ngx_http.c(ngx_http_module)、ngx_mail.c(ngx_mail_module)这是目前官方的6大核心类型模块。以及比较特殊的配置类型模块ngx_conf_file.c,是唯一一个只有1个模块的模块类型。

        2)type成员,定义当前模块是那种类型的模块。

          例如:NGX_HTTP_MODULE、NGX_EVENT_MODULE、NGX_MAIL_MODULE、NGX_CORE_MODULE、NGX_CONF_MODULE,官方指定就这5个模块

         3)ctx_index成员,表示当前模块在一类模块中的序号。也是一个请求处理的顺序。

        这个成员常常是有管理这类模块的一个核心模块设置的。例如:HTTP模块,ctx_index是有核心模块ngx_http.c(ngx_http_module)设置的,有兴趣同学可以参考147行代码。ngx_event.c (ngx_events_module),ngx_mail.c(ngx_mail_module)这些模块是一样的。nginx.c(ngx_core_module)、ngx_log.c(ngx_errlog_module)、ngx_event_openssl.c(ngx_openssl_module)这三个模块数量固定,不需要动态指定。

         4)index成员,当前模块在ngx_modules数组中的序号,也就算在所有模块中的序号。

        有兴趣的同学可以看nginx.c中的main(int argc, char *const *argv)方法263行。

2,在nginx模块开发中大部分都是http模块,那么我们来看一下http模块的结构体 ngx_http_module_t 

       HTTP框架在读取、重载配置时定义了ngx_http_module_t,该结构体对应当前HTTP模块启动过程的8个阶段

typedef struct {
//解析配置文件前调用
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
//完成配置文件的解析后调用
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
//当需要创建用于存储main级别的全局配置項时。(直属于虚拟主机http{...})
    void       *(*create_main_conf)(ngx_conf_t *cf);
//初始化main基本配置項
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);
//当需要创建用于存储srv级别的配置項时。(直属于虚拟主机server{...})
    void       *(*create_srv_conf)(ngx_conf_t *cf);
//合并main级别和srv级别的配置項
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
//当需要创建用于存储loc级别的配置項时。(直属于location{...})
    void       *(*create_loc_conf)(ngx_conf_t *cf);
//合并srv级别和loc级别的配置項
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

3,commands数组用于定义模块的配置文件参数,每一个数组元素都是ngx_command_t类型

struct ngx_command_s {
//配置項名称,也就是nginx.conf中的配置项名称。例如:gzip  upstream
    ngx_str_t             name;
//配置項类型,有三个作用:1,指明该配置項可以出现的位置(http server location ),2,可以携带参数的个数,3,参数出现的形式。用“|”分割。
    ngx_uint_t            type;
//处理配置項的回调方法。系统预设有回调方法,可以直接使用。
//cf: 该参数里面保存从配置文件读取到的原始字符串以及相关的一些信息。
//cmd: 这个配置指令对应的ngx_command_t结构。
//conf: 就是定义的存储这个配置值的结构体
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//配置項在内存中的相对偏移位置,因为http模块对所有http模块所要保存的配置信息,划分了main, server和location三个地方进行存储,参数应该保存到那个地方存储,就看这个参数值。这里可能的值为 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。当然也可以直接置为0,就是NGX_HTTP_MAIN_CONF_OFFSET。
    ngx_uint_t            conf;
//指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。因为对于配置信息的存储,一般我们都是定义个结构体来存储的。那么比如我们定义了一个结构体A,该项配置的值需要存储到该结构体的b字段。那么在这里就可以填写为offsetof(A, b)。对于有些配置项,它的值不需要保存或者是需要保存到更为复杂的结构中时,这里可以设置为0。如果使用nginx预设的解析配置方法,就必须设在offset
    ngx_uint_t            offset;
//大多数时候,都不需要,所以简单地设为0即可
    void                 *post;
};

type参数字典表:

    第一种作用:

    NGX_DIRECT_CONF:可以出现在配置文件中最外层。例如已经提供的配置指令daemon,master_process等。

    NGX_MAIN_CONF: http、mail、events、error_log等。

    NGX_ANY_CONF: 该配置指令可以出现在任意配置级别上。

    对于我们编写的大多数模块而言,都是在处理http相关的事情,也就是所谓的都是NGX_HTTP_MODULE,对于这样类型的模块,其配置可能出现的位置也是分为直接出现在http里面,以及其他位置。

    NGX_HTTP_MAIN_CONF: 可以直接出现在http配置指令里。

    NGX_HTTP_SRV_CONF: 可以出现在http里面的server配置指令里。

    NGX_HTTP_LOC_CONF: 可以出现在http server块里面的location配置指令里。

    NGX_HTTP_UPS_CONF: 可以出现在http里面的upstream配置指令里。

    NGX_HTTP_SIF_CONF: 可以出现在http里面的server配置指令里的if语句所在的block中。

    NGX_HTTP_LMT_CONF: 可以出现在http里面的limit_except指令的block中。

    NGX_HTTP_LIF_CONF: 可以出现在http server块里面的location配置指令里的if语句所在的block中。

    第二种作用:

    NGX_CONF_NOARGS:配置指令不接受任何参数。

    NGX_CONF_TAKE1:配置指令接受1个参数。

    NGX_CONF_TAKE2:配置指令接受2个参数。

    NGX_CONF_TAKE3:配置指令接受3个参数。

    NGX_CONF_TAKE4:配置指令接受4个参数。

    NGX_CONF_TAKE5:配置指令接受5个参数。

    NGX_CONF_TAKE6:配置指令接受6个参数。

    NGX_CONF_TAKE7:配置指令接受7个参数。

    可以组合多个属性,比如一个指令即可以不填参数,也可以接受1个或者2个参数。那么就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。如果写上面三个属性在一起,你觉得麻烦,那么没有关系,nginx提供了一些定义,使用起来更简洁。

    NGX_CONF_TAKE12:配置指令接受1个或者2个参数。

    NGX_CONF_TAKE13:配置指令接受1个或者3个参数。

    NGX_CONF_TAKE23:配置指令接受2个或者3个参数。

    NGX_CONF_TAKE123:配置指令接受1个或者2个或者3参数。

    NGX_CONF_TAKE1234:配置指令接受1个或者2个或者3个或者4个参数。

    第三种作用:

    NGX_CONF_1MORE:配置指令接受至少一个参数。

    NGX_CONF_2MORE:配置指令接受至少两个参数。

    NGX_CONF_MULTI: 配置指令可以接受多个参数,即个数不定。

    NGX_CONF_BLOCK:配置指令可以接受的值是一个配置信息块。也就是一对大括号括起来的内容。里面可以再包括很多的配置指令。比如常见的server指令就是这个属性的。

    NGX_CONF_FLAG:配置指令可以接受的值是”on”或者”off”,最终会被转成bool值。

    NGX_CONF_ANY:配置指令可以接受的任意的参数值。一个或者多个,或者”on”或者”off”,或者是配置块。


 *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)预设的回调方法:

    ngx_conf_set_flag_slot: 读取NGX_CONF_FLAG类型的参数。

    ngx_conf_set_str_slot:读取字符串类型的参数。

    ngx_conf_set_str_array_slot: 读取字符串数组类型的参数。

    ngx_conf_set_keyval_slot: 读取键值对类型的参数。

    ngx_conf_set_num_slot: 读取整数类型(有符号整数ngx_int_t)的参数。

    ngx_conf_set_size_slot:读取size_t类型的参数,也就是无符号数。

    ngx_conf_set_off_slot: 读取off_t类型的参数。

    ngx_conf_set_msec_slot: 读取毫秒值类型的参数。

    ngx_conf_set_sec_slot: 读取秒值类型的参数。

    ngx_conf_set_bufs_slot: 读取的参数值是2个,一个是buf的个数,一个是buf的大小。例如: output_buffers 1 128k;

    ngx_conf_set_enum_slot: 读取枚举类型的参数,将其转换成整数ngx_uint_t类型。

    ngx_conf_set_bitmask_slot: 读取参数的值,并将这些参数的值以bit位的形式存储。例如:HttpDavModule模块的dav_methods指令。

4,HTTP请求的11个处理阶段

    nginx模块设计使得每一个HTTP模块只专注自己完成的任务。而一个HTTP请求,可能就需要有无数个HTTP模块共同合作来完成。每个模块什么时候介入处理请求,在那个模块前,在那个模块后。这时就需要nginx把一个HTTP的完整请求划分出先后顺序。这就有了HTTP处理的11个阶段,这11个阶段包括系统模块阶段,和用户开发模块处理阶段。为了防止系统的稳定性,nginx支持第三方模块介入的只有7个阶段。

以下定义的11个阶段时有顺序的,一定时按照定义的顺序执行的

typedef enum {
       //1)HTTP框架在建立的TCP连接上收到完整HTTP请求头部时。
    NGX_HTTP_POST_READ_PHASE = 0,
        //2)Server请求地址重写阶段
    NGX_HTTP_SERVER_REWRITE_PHASE,
        //3)配置查找阶段 (该阶段不允许其他模块介入)
    NGX_HTTP_FIND_CONFIG_PHASE,
        //4) location请求地址重写阶段
    NGX_HTTP_REWRITE_PHASE,
        //5)请求地址重写提交阶段(该阶段不允许其他模块介入)
    NGX_HTTP_POST_REWRITE_PHASE,
        //6)访问权限检查准备阶段
    NGX_HTTP_PREACCESS_PHASE,
        //7)访问权限检查阶段
    NGX_HTTP_ACCESS_PHASE,
        //8)访问权限检查提交阶段(该阶段不允许其他模块介入)
    NGX_HTTP_POST_ACCESS_PHASE,
        //9)配置项try_files处理阶段(该阶段不允许其他模块介入)
    NGX_HTTP_TRY_FILES_PHASE,
        //10) 内容产生阶段
    NGX_HTTP_CONTENT_PHASE,
        //11)日志模块处理阶段
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

模块handler的介入方式有两种,

    1)通过向全局的结构体ngx_http_core_main_conf_t的phases数组中添加handler。 例如:ngx_http_realip_module.c模块。该方法挂载的动作一般是在模块上下文调用的postconfiguration函数中,介入的handler在每次请求的时候都会被调用。该方法也同时使用所有阶段。

    2)把希望处理的handler方法设置到location相关的ngx_http_core_loc_conf_t结构体中handler指针中。每一个location对应这一个独立的ngx_http_core_loc_conf_t。例如:ngx_http_perl_module.c,ngx_http_flv_module.c等模块。该方法挂载的动作不必在postconfiguration中,可以在ngx_command_t的某个配置項的回调方法中添加处理方法。如果两种方法同时设在回调方法,第一种方法设置的将会被覆盖。如果一个location中有多个模块,在前面模块设置的handler将会被覆盖。这种方法之只使用于NGX_HTTP_CONTENT_PHASE阶段,并且只对指定的location生效。一般情况下,我们自定义的模块,都使用这种方式挂载在NGX_HTTP_CONTENT_PHASE阶段的。

    


转载于:https://my.oschina.net/guisz/blog/612684

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值