nginx http模块开发(4) - ngx_http_core_location

2018-07-20 17:02:40

在上一节的 ngx_http_core_server 函数里在做完 server{} 块的初始化操作后,又开始遍历解析该块里的其他指令。

// 递归解析server{}块里 http模块配置
rv = ngx_conf_parse(cf, NULL);

当解析到 location{} 块指令时,根据 ngx_http_core_module 模块的配置

static ngx_command_t  ngx_http_core_commands[] = {
    ...

    // 定义location
    { 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_null_command
};

就会调用 ngx_http_core_location 函数进行初始化。

static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char                      *rv;
    u_char                    *mod;
    size_t                     len;
    ngx_str_t                 *value, *name;
    ngx_uint_t                 i;
    ngx_conf_t                 save;
    ngx_http_module_t         *module;
    ngx_http_conf_ctx_t       *ctx, *pctx;
    ngx_http_core_loc_conf_t  *clcf, *pclcf;

    // ctx是本location的配置结构体数组
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    // 保存server{}或上一层location{}的配置上下文
    // 也就是server{}/location{}里的ngx_http_conf_ctx_t
    pctx = cf->ctx;

    // main_conf指针直接指向上层http_ctx,复用整个数组,无需再分配内存
    ctx->main_conf = pctx->main_conf;

    // srv_conf指针直接指向上层http_ctx,复用整个数组,无需再分配内存
    ctx->srv_conf = pctx->srv_conf;

    // 重新分配内存location配置数组,存储location基本的配置
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    // 调用每个http模块的create_xxx_conf函数,创建配置结构体
    for (i = 0; cf->cycle->modules[i]; i++) {
        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[i]->ctx;

        // 创建每个模块的loc_conf
        if (module->create_loc_conf) {
            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =
                                                   module->create_loc_conf(cf);
            if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {
                return NGX_CONF_ERROR;
            }
        }
    }

    // 获取本location{}里http core模块的location配置,它记录了本location的核心参数
    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];

    // http core模块的配置ctx保存了本location{}的配置数组
    // 以后通过它就可以获取本location{}的全部模块配置
    clcf->loc_conf = ctx->loc_conf;

指针大致如下图

剩余代码

// 检查location的名字
    value = cf->args->elts;

    // 解析 = ^~ ~ ~* @ 等正则标识符
    if (cf->args->nelts == 3) {
        ...
    } else {
        // 这里是标识符与名字连在一起
        // 例如 =root {}
        ...
    }

    // 上一个层次里的http core模块的loc配置,存储所有的location{}
    // 通常就是server{}
    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];

    // 处理嵌套location的情况,这里暂不研究
    if (cf->cmd_type == NGX_HTTP_LOC_CONF) {
        ...
    }

    // 把本location{}的配置信息加入到上一级的配置里
    // pclcf->locations是一个queue
    // in ngx_http.c
    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    // location{}的解析环境已经准备好,下面开始解析location{}配置

    // 暂存当前的解析上下文
    save = *cf;

    // 设置事件模块的新解析上下文
    // 即本location{}的ngx_http_conf_ctx_t结构体
    cf->ctx = ctx;

    // 设置解析的信息,类型在之前的http解析已经设置为NGX_HTTP_MODULE
    cf->cmd_type = NGX_HTTP_LOC_CONF;

    // 递归解析http模块
    rv = ngx_conf_parse(cf, NULL);

    // 恢复之前保存的解析上下文
    // 可以解析下一个location{}
    *cf = save;

    return rv;
}

在 ngx_http_core_loc_conf_t 结构体中有一个 locations 成员,它表示属于当前块的所有 location 块通过 ngx_http_location_queue_t 结构体构成的双向链表。

typedef struct {

    //双向链表,把所有的 ngx_http_location_queue_t 连接起来。
    ngx_queue_t                      queue;

    //如果location中的字符串可以精确匹配(包括正则表达式),它将指向对应的
    // ngx_http_core_loc_conf_t,否则为NULL。
    ngx_http_core_loc_conf_t        *exact;

    //如果location中的字符串无法精确匹配(包括自定义通配符),它将指向对应的
    // ngx_http_core_loc_conf_t,否则为NULL。
    ngx_http_core_loc_conf_t        *inclusive;

    ngx_str_t                       *name;
    u_char                          *file_name;
    ngx_uint_t                       line;
    ngx_queue_t                      list;
} ngx_http_location_queue_t;

可以看到, ngx_http_location_queue_t 中的 queue 成员将把所有相关的 ngx_http_location_queue_t 结构体串联起来。同时, ngx_http_location_queue_t 将帮助用户把所有的 location 块与其所属的 server块关联起来。 

假设 serverA 下有 location L1 和 locationL2。

serverA 以及其下所属的 location L1 和 location L2 共包括3个 ngx_http_core_loc_conf_t 结构体,它们是相关的,下面看看它们是怎样关联起来的。

每一个  ngx_http_core_loc_conf_t 都将对应着一个 ngx_http_location_queue_t  结构体。在 serverA 拥有的 ngx_http_core_loc_conf_t 结构体中,locations成员将指向它所属的 ngx_http_location_queue_t  结构体,这是1个双向链表的首部。当解析到location L1 块时,会创建一个 ngx_http_location_queue_t  结构体并添加到 locations 双向链表的尾部,该 ngx_http_location_queue_t  结构体中的 exact 或者 inclusive 指针将会指向 location L1 所属的 ngx_http_core_loc_conf_t 结构体(在 location后的表达式属于完全匹配时, exact指针有效,否则表达式将带有通配符,这时 inclusive有效。 exact优先级高于 inclusive),这样就把 location L1 块对应的 ngx_http_core_loc_conf_t 结构体,以及其 loc_conf 成员指向的所有HTTP模块在 location L1 块内的配置项与 server A块结合了起来。解析到 location L2时会做相同处理,这也就得到下图。


 备注

1.测试环境centos7 64位,nginx版本为 1.14.0。
2..原文地址http://www.freecls.com/a/2712/cd


©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。