nginx配置解析原理(一)

来源:互联网 发布:众知体育是正品吗 编辑:程序博客网 时间:2024/06/05 06:44

概述

这几篇博客文章我们将会重点分析nginx配置项解析的原理。因为nginx配置框架设计的非常灵活和强大,这就使得我们在分析其内部机制的时候带来了不小的挑战,而这个系列的博客的意义就在于梳理其内部实现,并整理出大致框架,以分享给读者。

作者之前看过网上的一些博客写的关于nginx配置框架的梳理,粗制滥造,因此萌生了自己写的想法。希望大家能提出宝贵意见。

在讲配置之前,我们不得不简单说下nginx模块的概念。因为nginx属于微内核设计,nginx的强大之处在于灵活的微内核再加上可扩展的模块,nginx自身的模块有core、event、http、mail等核心模块,但是开发者又可以基于这些核心模块开发满足自身业务需求的模块,首当其冲的便是http模块了,因此,大家在阅读nginx代码的时候可以看到很多的ngx_http_xxx,这些都是基于http模块开发的第三方模块,而我们后面局里的重点也将会是http,谁叫他这么重要呢。

好了,言归正传,我们来看看nginx如何管理复杂的配置项吧。我在这里假设各位看官对nginx配置项的形式有了初步的了解,如果还没有配置过nginx的,那么请先移步,自行脑补再回头。

我们在本篇博客中会详细描述nginx配置解析的源头,从头干起,可避免在后面解析的时候的突兀感。

nginx的配置项的老祖宗就藏在下面这个数据结构了

struct ngx_cycle_s {    void                  ****conf_ctx;    ......}
当时看到这个结构的时候,我承认我有种淡淡的忧伤,我自认c语言也还不错,但是还是花了点时间才整明白这个四重指针到底是怎么指来指去的。而这个也是我们拨云见雾的第一步,我们可以并且必须搞清楚这鸟玩意到底是干嘛的。

首先看看它是如何诞生的:

<pre name="code" class="cpp">ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle){
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *)); if (cycle->conf_ctx == NULL) { ngx_destroy_pool(pool); return NULL; } ......}
从这里我们看以看到,conf_ctx是一个指针数组,数组一共有ngx_max_module项(也就是nginx有多少个模块数组就有多大)。好了,除此之外,我们看不出更多信息。所以目前为止我们看到的结构如下:

那么接下来我们的任务就是弄清楚这个void*数组里面的每一项到底指向了谁

for (i = 0; ngx_modules[i]; i++) {        if (ngx_modules[i]->type != NGX_CORE_MODULE) {            continue;        }        module = ngx_modules[i]->ctx;        if (module->create_conf) {            rv = module->create_conf(cycle);            if (rv == NULL) {                ngx_destroy_pool(pool);                return NULL;            }            cycle->conf_ctx[ngx_modules[i]->index] = rv;        }    }   ......}
在这里,会调用每个模块的create_conf()方法,将结果保存在数组的对应项中。如core模块的ngx_core_module_create_conf(),然而对于大部分模块来说,其实并没有实现该方法,如我们关注的http_core模块,因此http_core模块对应的该项其实还是为NULL。此时数据结构图如下:


前面说过,对nginx的配置解析我们以http模块为例来说明,因此,接下来我们就来看看http模块的配置是如何一步步的被加载的。

在完成了配置数据结构的初始化以后,接下来就进入配置解析阶段了,我们不关心nginx是如何回调各模块的配置解析方法。我们就来看看nginx如何解析http配置。

首先,在nginx的配置设计中http模块的配置项位于下面的block

http{    ......}
nginx遇到"http"指令时,会调用该指令的解析函数ngx_http_block

static char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){    ...    /* the main http context */    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));    if (ctx == NULL) {        return NGX_CONF_ERROR;    }    *(ngx_http_conf_ctx_t **) conf = ctx;    /* count the number of the http modules and set up their indices */    ngx_http_max_module = 0;    for (m = 0; ngx_modules[m]; m++) {        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {            continue;        }        ngx_modules[m]->ctx_index = ngx_http_max_module++;    }    ......}

上面函数有一句非常重要

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));*(ngx_http_conf_ctx_t **) conf = ctx;
首先为http模块分配一个ngx_http_conf_ctx_t结构,然后再将该结构存储在conf指针指向的内存处,而追溯conf指针的来源是ngx_conf_handler():

if (cmd->type & NGX_DIRECT_CONF) {    conf = ((void **) cf->ctx)[ngx_modules[i]->index];    } else if (cmd->type & NGX_MAIN_CONF) {        conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);// 对于http 块内的解析会进入该分支        } else if (cf->ctx) {            confp = *(void **) ((char *) cf->ctx + cmd->conf);             if (confp) {                conf = confp[ngx_modules[i]->ctx_index];            }        }        rv = cmd->set(cf, cmd, conf);
由于"http"是http_core模块的指令,cmd->type满足NGX__MAIN_CONF,因此这里的conf其实就代表了cycle->conf_ctx[ngx_http_core_module]这项的地址,而

*(ngx_http_conf_ctx_t **) cof = ctx就是将ctx(指向ngx_http_conf_ctx结构的指针)放在数组的这一项里面,此时形成的结构如下图:

而接下来又会初始化该ngx_http_conf_ctx_t这个结构

ctx->main_conf = ngx_pcalloc(cf->pool,                                 sizeof(void *) * ngx_http_max_module);ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
形成的结构如下:

我想,话说到这个份上,大家应该能理解为什么cycle_conf_ctx是四重指针了吧。

0 0
原创粉丝点击