Nginx HTTP模块的配置项管理【2】

来源:互联网 发布:经典的心理学书籍知乎 编辑:程序博客网 时间:2024/06/15 06:28

    本篇文章将具体介绍一下Nginx解析HTTP模块配置文件的流程。

    核心模块ngx_http_module是进入HTTP模块解析的入口。该模块仅仅定义了一个ngx_command_t结构体,用于声明遇到http{}时需要进入的处理函数

static ngx_command_t  ngx_http_commands[] = {    { ngx_string("http"),      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,      //开始解析http模块相关的配置项  ngx_http_block,      0,      0,      NULL },      ngx_null_command};

     而ngx_http_core_module则定义了默认的配置文件的解析规则、create_XXX_conf生成的结构体的类型等。先说一下所有级别的配置项是如何在内存中关联起来的,ngx_http_core_module定义了三种配置数据结构ngx_http_core_main_conf_t、ngx_http_core_srv_t、ngx_http_core_loc_t。对于ngx_http_core_main_conf_t来说,它包含一个servers动态数组,用于指向所有由ngx_http_core_module创建的ngx_http_core_srv_conf_t数据结构。而ngx_http_core_srv_conf_t 数据结构中又包含了指向它上下文的指针;而ngx_http_core_loc_conf_t中有一个locations的双向循环链表,它可以将不同级别的属于location的配置项关联起来。借用《Nginx模块开发与架构解析》中的图:

    

 

 

 

    

    这样的关系是ngx_http_core_module中定义的。但是由于ngx_http_conf_ctx的关联,可以找到所有模块的配置结构体。


http{}级别解析  

 所以当遇到http{}时,调用ngx_http_block函数,在这个函数中,将会调用ngx_http_module_t公共接口中的回调函数,其中关于配置项解析的处理流程是这样的:

    1、创建配置上下文结构体ngx_http_conf_ctx_t,并为其包含的三个数组分配空间。

    2、对于每一个HTTP模块,如果定义了create_main_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的main_conf数组中;如果定义了create_srv_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的srv_conf数组中;如果定义了create_loc_conf函数,则调用它,将产生的结构体按照模块的序号加入到ngx_http_conf_ctx_t的loc_conf数组中。

    3、调用每个HTTP模块的preconfiguration函数。

    4、调用ngx_conf_parse函数真正进行解析,该函数扫描配置文件,遇到模块的ngx_command_t中匹配的配置项时,调用其set函数,进行参数值的提取或者继续进行server级别和main级别配置块的解析。执行完这个函数后,各个配置结构体的成员的值已经填充完毕。

    5、调用每个模块的init_main_conf函数。

    6、调用merge_srv_conf和merge_loc_conf函数进行配置项的合并。合并包括http与srv级别的server有关的配置项的合并、http与server级别的location有关的配置项合并、以及server与location级别的location相关的配置项合并以及location级别嵌套的location相关配置项的合并(比较绕。。。)。我们在上面介绍了,由于它们以一种比较复杂的关系关联起来,可以很方便的将不同级别的配置结构体做合并操作。

   ngx_http_core_module定义了很多个ngx_command_t,我大致列举了几个:

  

static ngx_command_t  ngx_http_core_commands[] = {...    { ngx_string("server"),      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS,      ngx_http_core_server,      0,      0,      NULL },  //本配置项属于server相关的配置项,但它可以是main级别的  //也可以是srv级别的    { ngx_string("connection_pool_size"),      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,      ngx_conf_set_size_slot,  //#define NGX_HTTP_SRV_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, srv_conf)      NGX_HTTP_SRV_CONF_OFFSET,  //connecton_pool_size相对于ngx_http_core_srv_conf_t的偏移  //ngx_http_core_pool_size_p解析完配置项的后续handler函数  offsetof(ngx_http_core_srv_conf_t, connection_pool_size),      &ngx_http_core_pool_size_p },    ...    { ngx_string("location"),      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,      ngx_http_core_location,      NGX_HTTP_SRV_CONF_OFFSET,      0,      NULL },    { ngx_string("listen"),      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,      ngx_http_core_listen,      NGX_HTTP_SRV_CONF_OFFSET,      0,      NULL },    { ngx_string("server_name"),      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,      ngx_http_core_server_name,      NGX_HTTP_SRV_CONF_OFFSET,      0,      NULL },    ...#endif      ngx_null_command};

 

server级别解析


可见,在解析http级别的配置项时,遇到server{}时,会调用ngx_http_core_module模块进行server级别的配置项的解析。与http级别配置项解析相同,它也会生成一个ngx_http_conf_ctx_t结构体。

    具体流程如下:

    1、创建ngx_http_conf_ctx_t结构体,main_conf将指向http块下的ngx_http_conf_ctx_t中的main_conf指针,而另两个数组重新分配空间。

    2、通过调用create_srv_conf和create_loc_conf函数,按照模块的ctx_index加入到ngx_http_conf_ctx_t结构体的后两个数组中。

    3、HTTP模块只有一个http级别的ngx_http_conf_ctx_t上下文,这是全局的。将由ngx_http_core_module模块生成的ngx_http_core_srv_conf_t结构体添加到全局

ngx_http_core_main_conf_t的servers数组中。便于以后将http级别与server级别的有关server配置项的合并。

    4、继续调用ngx_conf_parse函数解析。

static char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy){    char                        *rv;    void                        *mconf;    ngx_uint_t                   i;    ngx_conf_t                   pcf;    ngx_http_module_t           *module;    struct sockaddr_in          *sin;    ngx_http_conf_ctx_t         *ctx, *http_ctx;    ngx_http_listen_opt_t        lsopt;    ngx_http_core_srv_conf_t    *cscf, **cscfp;    ngx_http_core_main_conf_t   *cmcf;    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));    if (ctx == NULL) {        return NGX_CONF_ERROR;    }    http_ctx = cf->ctx;    ctx->main_conf = http_ctx->main_conf;    /* the server{}'s srv_conf */    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);    if (ctx->srv_conf == NULL) {        return NGX_CONF_ERROR;    }    /* the server{}'s loc_conf */    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);    if (ctx->loc_conf == NULL) {        return NGX_CONF_ERROR;    }    for (i = 0; ngx_modules[i]; i++) {        if (ngx_modules[i]->type != NGX_HTTP_MODULE) {            continue;        }        module = ngx_modules[i]->ctx;//只调用两个函数了        if (module->create_srv_conf) {            mconf = module->create_srv_conf(cf);            if (mconf == NULL) {                return NGX_CONF_ERROR;            }            ctx->srv_conf[ngx_modules[i]->ctx_index] = mconf;        }        if (module->create_loc_conf) {            mconf = module->create_loc_conf(cf);            if (mconf == NULL) {                return NGX_CONF_ERROR;            }            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;        }    }    /* the server configuration context */    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];    cscf->ctx = ctx;//ctx->main_conf指向http级别的ngx_http_conf_ctx_t的main_conf    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];//返回的是要插入的内存地址    cscfp = ngx_array_push(&cmcf->servers);    if (cscfp == NULL) {        return NGX_CONF_ERROR;    }//插入cscf    *cscfp = cscf;    /* parse inside server{} */    pcf = *cf;//重要 更新上下文!以便调用下面的ngx_conf_parse的时候上下文信息//与当前级别相符。    cf->ctx = ctx;    cf->cmd_type = NGX_HTTP_SRV_CONF;    rv = ngx_conf_parse(cf, NULL);    *cf = pcf;    if (rv == NGX_CONF_OK && !cscf->listen) {        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));        sin = &lsopt.u.sockaddr_in;        sin->sin_family = AF_INET;#if (NGX_WIN32)        sin->sin_port = htons(80);#else        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);#endif        sin->sin_addr.s_addr = INADDR_ANY;        lsopt.socklen = sizeof(struct sockaddr_in);        lsopt.backlog = NGX_LISTEN_BACKLOG;        lsopt.rcvbuf = -1;        lsopt.sndbuf = -1;#if (NGX_HAVE_SETFIB)        lsopt.setfib = -1;#endif        lsopt.wildcard = 1;        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,                             NGX_SOCKADDR_STRLEN, 1);        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {            return NGX_CONF_ERROR;        }    }    return rv;}


location级别解析

    在解析srv级别配置项时,如果发现location{}配置块则调用ngx_http_core_location方法。

(1) 在解析location{}配置块时,仍然会像解析Http块一样,先建立ngx_http_conf_ctx_t结构体,只是这里的main_confsrv_conf都将指向所属的server块下的ngx_http_conf_ctx_t结构体的main_confsrv_conf指针数组,而loc_conf则将指向重新分配的指针数组。

(2) 循环调用所有HTTP模块的create_loc_conf方法,将返回的结构体指针按照模块序号ctx_index保存到上述的loc_conf指针数组中。

(3) 如果在location中使用了正则表达式,那么这时将调用pcre_compile方法预编译正则表达式,提高性能。

(4) 第一个HTTP模块是ngx_http_core_module模块,它在create_loc_conf方法中将会生成ngx_http_core_loc_conf_t配置结构体,可以认为该结构体对应着当前解析的location块。这时会生成ngx_http_location_queue_t结构体,因为每一个ngx_http_core_loc_conf_t结构体都对应着一个ngx_http_location_queue_t,因此,此处将把ngx_http_location_queue_t串联成双向链表。

(5) 正式解析当前location{}配置块内的loc级别配置项。

 

配置项合并

   合并就不说了,就是用到了结构体中特有servers、ctx以及locations这些成员将所有配置项关联起来。

 

 

 

 

 

   

0 0
原创粉丝点击