nginx配置解析原理(一)

概述

这几篇博客文章我们将会重点分析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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值