nginx配置解析原理(二)

本文深入探讨Nginx配置文件的解析原理,包括http模块配置解析及内存映射,详细解析了ngx_http_block和ngx_http_core_server函数的工作流程。

前面的nginx配置原理解析(一)我们重点阐述了nginx配置的起源,同时以http模块的配置为例勾勒了nginx配置数据结构在内存中的映像。本文继续(一)未完成的部分,仔细描述http块内的配置解析,同时为读者勾画整个解析完成后内存配置结构。

预备知识

nginx http模块的配置一般形式如下:

http {
    test_str "hi"
    ...
    server {
        ...
        test_str "how"
        location /hello {
            ...
        }
        location /hi {
            ...
            test_str "how are u"
        }
        ...
    }

    server {
        location /x {
            ...
        }
        location /y {
            ...
        }
    } 
}

http 块下面可以包含server块,而server块内也可嵌套location块,同时,每个模块的可以根据自己的需求将同一个配置同时出现在http、server、location块中,但是后续也必须得由自己决定,如上面示例的test_str。

nginx配置解析过程

在这里,我们简单描述下nginx配置解析过程以及函数调用关系。首先,nginx由许多模块构成,每个配置项应该是由其中的某个模块来解析,因此,整个模块解析过程其实就是“加载-发现-解析”的循环。

所有的模块配置解析都是由ngx_conf_parse()发起的。在这里先调用ngx_read_token()读取一个配置项,然后调用ngx_conf_handler()来解析配置项。以http模块为例,如遇到"http"这个指令时,会调用ngx_http_block()来解析整个http模块的配置。

关于ngx_http_block()函数初始化我们已经在(一)中仔细描述过了,并且给出了在内存中的映像图,今天我们就接着这个继续往下。

ngx_http_block

在(一)中我们讲到了ngx_http_block()过程的初始化,但是具体的解析我们未涉及。

for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;
        mi = ngx_modules[m]->ctx_index;

        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        if (module->create_loc_conf) {
            ctx->loc_conf[mi] = module->create_loc_conf(cf);
            if (ctx->loc_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }
这里我们调用每个模块的三个回调函数(不一定需要全部实现),并在内存中创建其映像关系。之所以需要创建loc_conf、server_conf、main_conf是因为nginx的某个配置可能会同时出现在main、server、location范围,我们需要创建这些映像以便于合并。

当准备工作完成以后,会正式进入配置解析流程

/* parse inside the http{} block */

    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;
    rv = ngx_conf_parse(cf, NULL);

    if (rv != NGX_CONF_OK) {
        goto failed;
    }

还是调用ngx_conf_parse()来解析配置,只是此时配置环境的上下文已经发生改变,cf->module_type = NGX_HTTP_MODULE,cf->cmd_type = NGX_HTTP_MAIN_CONF。表示此时要解析的是http模块的main block。

ngx_http_core_server

我们接下来重点研究对"server"指令的解析。整个调用的路径又变成了ngx_conf_parse()->ngx_conf_handler()。而ngx_conf_handler搜索到"server"指令的处理函数其实是由ngx_http_core_module的ngx_http_core_server()方法来解析。我们只关注这个函数最核心的部分:

static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
 
    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;

    // cmcf是http_ctx->main_conf[ngx_http_core_module]
    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

    // 这个函数获取到数组第一个可用的项
    cscfp = ngx_array_push(&cmcf->servers);
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }

    *cscfp = cscf;


    /* parse inside server{} */

    pcf = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_SRV_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;
    return rv;
}
这个函数
  1. 首先为自己分配了一个ngx_http_conf_ctx_t结构;
  2. 初始化该结构,将该结构的main_conf指向http_ctx的main_conf,同时创建srv_conf和loc_conf指针数组,
  3. 依此调用每个http模块的create_srv_conf和create_loc_conf的回调方法(可不必实现),

接下来,就解析位于server{}块内的配置了,解析的方法也是调用ngx_conf_parse,只是此时的上下文环境又发生了改变:cf->ctx = ctx;cf->cmd_type = NGX_HTTP_SRV_CONF。同时在此之前,会将该server对应的ngx_http_conf_ctx_t结构放在cmcf->servers数组中:

    // cmcf是http_ctx->main_conf[ngx_http_core_module]
    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

    // 这个函数获取到数组第一个空闲项
    cscfp = ngx_array_push(&cmcf->servers);
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }

    *cscfp = cscf;

用文字来表述也就是:在http{}对应的ngx_http_core_module的main配置ngx_http_core_main_conf_t中有一个数组servers存储了http{}块下的所有的server的结构,主要是便于后续merge。以后在解析的时候没遇到一个server {},就会将该server对应的数据结构添加到servers数组中。

至此,nginx解析到这里的时候在内存中形成的配置项结构如下图所示:


对上图的理解:

  1. 对于http模块的每个层级配置,均有一个ngx_http_conf_ctx_t结构与之对应,如"http"指令对应的粉色的方框以及两个server指令对应的蓝色方框;
  2. http块下所有的server块对应的ngx_http_conf_ctx_t结构均在存储在http块对应的ngx_http_conf_ctx_t的main_conf的ngx_http_core模块的ngx_http_core_main_conf_t的servers字段中,该字段的类型是array;
  3. 每个server的ngx_http_conf_ctx_t内又会创建srv_conf和loc_conf数组,以解析server{}内的配置,但不需要创建main_conf数组了,main_conf直接指向父配置块的main_conf,如图上的两条虚线

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值