nginx http模块开发(2) - ngx_http_block

2018-07-20 10:02:36

ngx_http_module 核心模块的 ngx_http_block 方法是在配置解析到 http{} 配置块时被调用的。

我们先来看看 nginx 的配置,一个http块可以有多个 server块,一个server块可以有多个location块。

root 指令既可以出现在 http 块,也可以出现在 server 块,location块。如果 location 里定义了,那么直接拿来用,如果没定义,就上上层 server 块里找,如果还是没定义,那么就去 http 块里找。为了实现这个功能,nginx 里就多个了配置合并的概念。而且也有如下的概念:

直接隶属于 http{} 块的称为 main 配置项。
直接隶属于 server{} 块的称为 srv 配置项。
直接隶属于 location{} 块的称为 loc 配置项。

http {
    sendfile        on;
    keepalive_timeout  65;
    
    root html;

    server {
        listen       80 default;
        rewrite ^(.*) http://www.freecls.com permanent;
    }
    
    server {
        listen       80;
        server_name  admin.freecls.com;

        location ~ (\.php)$ {
            root html/admin; #指定php的根目录
            fastcgi_pass 127.0.0.1:9000;#php-fpm的默认端口是9000
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }

        location = / {
            rewrite ^.*$ /index.php last;
        }
    }

}


先来看一下待会要用到的 ngx_http_conf_ctx_t 的定义。

// 有三个void*数组,存储三个层次的模块配置
// 所有http模块的配置都存储在这里
// main配置只有一个,即http{}
// srv有多个,每个server{}有一个
typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t;

ngx_http_block 代码如下。 

static char *
ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                        *rv;
    ngx_uint_t                   mi, m, s;
    ngx_conf_t                   pcf;
    ngx_http_module_t           *module;
    ngx_http_conf_ctx_t         *ctx;
    ngx_http_core_loc_conf_t    *clcf;
    ngx_http_core_srv_conf_t   **cscfp;
    ngx_http_core_main_conf_t   *cmcf;

    // http处理的配置结构体,里面有main_conf/srv_conf/loc_conf三个数组
    // 不允许重复配置
    if (*(ngx_http_conf_ctx_t **) conf) {
        return "is duplicate";
    }

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    // 类似于 cycle->conf_ctx[module index] = ctx
    *(ngx_http_conf_ctx_t **) conf = ctx;

    // 设置每个http模块的ctx_index,即自己的序号
    // 返回http模块的数量
    ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);

    // main配置数组,所有http模块只有一个
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    // srv配置数组,在http main层次存储server基本的配置,用于合并
    // 本身并无实际意义
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    // location配置数组,在http main层次存储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 (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[m]->ctx;
        mi = cf->cycle->modules[m]->ctx_index;

        // 创建每个模块的main_conf
        if (module->create_main_conf) {
            ctx->main_conf[mi] = module->create_main_conf(cf);
            if (ctx->main_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

        // 创建每个模块的srv_conf
        if (module->create_srv_conf) {
            ctx->srv_conf[mi] = module->create_srv_conf(cf);
            if (ctx->srv_conf[mi] == NULL) {
                return NGX_CONF_ERROR;
            }
        }

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

下图以第一个 http 模块 ngx_http_core_module 为例的图。

下面是 ngx_http_block 余下的代码。

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

    // 暂存当前的解析上下文
    // cf是函数入口传递来的上下文
    pcf = *cf;

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

    // 解析之前,调用每个http模块的 preconfiguration,可以添加变量定义
    for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

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

        if (module->preconfiguration) {
            if (module->preconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

    /* parse inside the http{} block */

    // 设置解析的类型等信息
    // NGX_HTTP_MODULE用来检查是否是http模块,防止用错了指令
    cf->module_type = NGX_HTTP_MODULE;
    cf->cmd_type = NGX_HTTP_MAIN_CONF;

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

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


    // 解析完毕,检查http{}里定义的server{}块
    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];

    // 所有server{}定义的配置都保存在core module main conf的serevrs数组里
    cscfp = cmcf->servers.elts;

    // 初始化每个http模块的main配置
    for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = cf->cycle->modules[m]->ctx;
        mi = cf->cycle->modules[m]->ctx_index;

        // 初始化main配置
        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }

        // 合并srv配置
        rv = ngx_http_merge_servers(cf, cmcf, module, mi);
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }


    /* create location trees */

    // 遍历server数组,创建location树
    for (s = 0; s < cmcf->servers.nelts; s++) {

        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];

        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }

        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }


    // 初始化http处理引擎的阶段数组,调用ngx_array_init
    // 虽然总共有11个阶段,但只初始化了7个数组,只能在这些里加handler
    // cmcf只有唯一的一个
    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }


    // 执行每个http模块的postconfiguration函数指针
    // 通常是向phase数组里添加handler
    for (m = 0; cf->cycle->modules[m]; m++) {
        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

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

        if (module->postconfiguration) {
            if (module->postconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

    // 初始化变量数组
    // in ngx_http_variables.c
    if (ngx_http_variables_init_vars(cf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    /*
     * http{}'s cf->ctx was needed while the configuration merging
     * and in postconfiguration process
     */

    // 恢复之前保存的解析上下文
    *cf = pcf;


    // 整理所有的http handler模块,填入引擎数组
    // 之前已经在模块的postconfiguration里添加过了
    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {
        return NGX_CONF_ERROR;
    }


    /* optimize the lists of ports, addresses and server names */

    // 类似stream模块,对已经整理好的监听端口数组排序
    // 调用ngx_create_listening添加到cycle的监听端口数组,只是添加,没有其他动作
    // 设置有连接发生时的回调函数ngx_http_init_connection
    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;

failed:

    // 解析错误,恢复之前保存的解析上下文
    *cf = pcf;

    return rv;
}

下一节讲解 ngx_http_core_module 模块的 server{}指令的解析。

 备注

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


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