Nginx http初始化(二)

刚开始我们要介绍一下http模块的定义:

typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);  //在解析配置文件http{}之前调用
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf); //在解析配置文件http{}之前调用 

    void       *(*create_main_conf)(ngx_conf_t *cf); //创建http模块的main config
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);   //初始化main config的时候调用

    void       *(*create_srv_conf)(ngx_conf_t *cf);  //创建sev config时候调用
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//合并http模块的server config,用于实现server config到main config的指令的继承、覆盖 

    void       *(*create_loc_conf)(ngx_conf_t *cf);  //创建loc config的时候调用
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);  //合并http模块的location config,用于实现location config到server config的指令的继承、覆盖 
} ngx_http_module_t;


前面一篇文章已经把http部分的配置结构讲的差不多了,这篇文章开始真正开始讲初始化了。我们已经知道http的初始化是从ngx_http_module模块的http命令的set回调函数ngx_http_block开始的,接下来就来分析这个函数吧:

//为http模块所有的配置分配内存空间
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }
//因为传进来的是ngx_http_module模块在全局变量cycle的ctx域中index处的指针,所以这句代码相当于是在cycle中为ngx_http_module的配置赋值,相当于是为http部分的配置占一个坑
    *(ngx_http_conf_ctx_t **) conf = ctx;

嗯,这段代码应该比较容易理解了,基本与event模块的初始化一个调调。说白了就是创建配置结构的指针,然后还要在cycle的ctx里面为ngx_http_module占一个坑。

//初始化所有http模块的模块分类索引
    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++;
    }
嗯,这段代码应该也能很容易理解吧,就是为所有的http模块初始化分类索引。
//未配置中的每一个项目分配内存空空间(这里是main_conf)
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }


    /*
     * the http null srv_conf context, it is used to merge
     * the server{}s' srv_conf'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 http null loc_conf context, it is used to merge
     * the server{}s' loc_conf'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;
    }
嗯,上面的代码也很简单,前面的文章已经提到,http模块比较奇葩的,其配置结构共有三种,对应于配置文件的三个地方,因而要为这三个地方都分配内存。

//循环遍历所有的http模块,为他们创建配置
    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;
//创建该http模块的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类型的模块,分别调用它们创建三种配置类型的函数,并根据它们的分类索引号保存到相应的地方。

	/* 先保存cf的副本,待所有http module的指令解析完再恢复 */  
	//这里之所以要备份,这是因为ngx_conf_parse函数式递归调用了,不能影响外层调用的环境
    pcf = *cf;
	/* 把解析http module的指令的上下文设置为ngx_http_conf_ctx_t */ 
	//这里之所以要这样做,这是因为在下一次的parse的时候,遇到命令的话,会传进去相应模块的配置,而ngx_conf_handler函数会在cf中寻找相应的模块的配置,然后再调用set回调函数
    cf->ctx = ctx;

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

        module = ngx_modules[m]->ctx;
		/* 调用http module的preconfiguration回调函数 */ 
        if (module->preconfiguration) {
            if (module->preconfiguration(cf) != NGX_OK) {
                return NGX_CONF_ERROR;
            }
        }
    }

    /* parse inside the http{} block */

    cf->module_type = NGX_HTTP_MODULE;   //这次解析的内容是http模块
    cf->cmd_type = NGX_HTTP_MAIN_CONF;   //命令的类型,为http命令的main
    rv = ngx_conf_parse(cf, NULL);   //还是解析http块命令里面的命令
嗯,这段代码也比较容易吧,这里之所以要将cf备份,原因上面也已经说的很明白了,因为是递归调用的,而cf变量的一些部分又是每次调用都共享的,所以里面的肯定不能影响外面的了。然后对cf进行赋值,指明接下来要解析的命令类型,模块类型等。然后还有就是调用所有模块的preconfiguration函数。

好了,这里应该必须要深入http命令内部的命令了,这里就分析两种重要的命令就行了,server与server里面的location命令了。

接下来我们来看看server的回调函数吧,这个函数是ngx_http_core_server,

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));   //为配置上下文分配内存空间,这里之所以要重新创建新的ngx_http_conf_ctx_t配置结构,是为了处理同时会有好几个server的情况,但是这些创建的ngx_http_conf_ctx_t结构的main_conf都是统一的
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    http_ctx = cf->ctx;
    ctx->main_conf = http_ctx->main_conf;  //这里用当前刚刚创建的配置上下文的main_conf指向以前的main_conf,这样可以保证main_conf的统一,因为main_conf只有一个

    /* the server{}'s srv_conf */
//创建当前server块的srv_conf的内存空间,因为同一个http里面可能会有多个server命令,因而要为每一个server指令都给所有的http模块进行配置
    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 */
//为当前server指令内的loc_conf分配内存
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }
这里首先创建的是ngx_http_conf_ctx_t类型的配置结构,这所以这里要重复创建这个结构,在上面的注释应该已经说的很清楚了,接下来就是为srv_conf以及loc_conf分配内存了。
//还是因为有多个server的存在,所以这里要继续要为每一个模块创建srv_conf与loc_conf
    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);  //创建http模块的srv_conf结构
            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);   //创建http模块的loc_conf
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }

            ctx->loc_conf[ngx_modules[i]->ctx_index] = mconf;
        }
    }
上部分代码就是遍历所有的http模块为他们创建配置了,
    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
    cscf->ctx = ctx;  //将当前创建的srv_conf结构的ctx指针指向当前创建的ngx_http_conf_ctx_t结构
    

    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];  //获取ngx_http_core_module的main_conf,它有一个servers数组,用来保存所有的server配置项,这里要知道的是main_conf 只有一个

    cscfp = ngx_array_push(&cmcf->servers);  //将刚刚创建的创建的ngx_http_core_module的srv_conf结构,即ngx_http_core_srv_conf_t结构保存入ngx_http_core_module模块的main_conf里面的servers数组当中
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }

    *cscfp = cscf;  //赋值,这里相当于是真正的压入到数组当中去
嗯,上面的代码需要注意一下,这里首先获得的是在当前创建的ngx_http_core_module模块的srv_conf,接着又获取ngx_http_core_module模块的main_conf,因为mian_conf是统一的,所以可以知道这里获取的其实是在http命令回调函数中创建的main_conf,然后再将刚刚创建的srv_conf结构压入到ngx_http_core_module模块的main_conf的servers域中,它用来保存所有server块的配置。
    pcf = *cf;  //这里还是备份cf结构,然后准备解析server块命令,这里之所以备份,是因为ngx_conf_parse函数式递归调用了,为了内层的不影响外层的
    cf->ctx = ctx;  //相当于是把新创建的配置向下文传入解析函数
    cf->cmd_type = NGX_HTTP_SRV_CONF;     //表示要解析的命令的类型

    rv = ngx_conf_parse(cf, NULL); //开始解析server块命令,这里面还涉及到location指令

    *cf = pcf;
接下来就可以开始解析server指令快内部的命令了,当然这里面肯定还会涉及到解析location命令。最后解析完成以后还要讲配置文件结构恢复,以免影响外层的解析。至于location指令的解析这里就不讲了,因为说实话,俺没有用过Nginx,所以我都不知道location指令是用来干嘛的。
 if (rv == NGX_CONF_OK && !cscf->listen) {   //表示当前所属的server块命令并没有用listen指令来申明监听端口,那么nginx会安排一个默认的监听?
        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));

        sin = &lsopt.u.sockaddr_in;

        sin->sin_family = AF_INET;
#if (NGX_WIN32)
        sin->sin_port = htons(80);  //默认80端口
#else
        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);
#endif
        sin->sin_addr.s_addr = INADDR_ANY;  //接收任意网卡连接

        lsopt.socklen = sizeof(struct sockaddr_in);

        lsopt.backlog = NGX_LISTEN_BACKLOG;
        lsopt.rcvbuf = -1;
        lsopt.sndbuf = -1;
#if (NGX_HAVE_SETFIB)
        lsopt.setfib = -1;
#endif
        lsopt.wildcard = 1;

        (void) ngx_sock_ntop(&lsopt.u.sockaddr, lsopt.addr,
                             NGX_SOCKADDR_STRLEN, 1);

        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {  //调用ngx_http_add_listen方法加入监听socket
            return NGX_CONF_ERROR;
        }
    }
嗯,上面的代码是,我们知道一般情况下server块命令里面会有listen命令用来指定http监听端口,这里的代码的意思就是说如果没有listen指令来指定端口的话,那么将会设置一个默认的端口,并调用ngx_http_add_listen函数来创建监听端口。

好了,这样server块命令就解析完了,接下来就可以回到http命令的回调函数了。

 /*
     * init http{} main_conf's, merge the server{}s' srv_conf's
     * and its location{}s' loc_conf's
     */

    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];  //获取ngx_http_core_module模块的main_conf配置
    cscfp = cmcf->servers.elts;  //获取包含的所有srv_conf结构

//遍历所有的http类型的模块,分别初始化他们的main_conf以及合并那些分散的srv_conf
    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;

        /* init http{} main_conf's */

        if (module->init_main_conf) {
            rv = module->init_main_conf(cf, ctx->main_conf[mi]);  //根据main_conf的内容进行初始化
            if (rv != NGX_CONF_OK) {
                goto failed;
            }
        }

        rv = ngx_http_merge_servers(cf, cmcf, module, mi);  //合并srv_conf的内容
        if (rv != NGX_CONF_OK) {
            goto failed;
        }
    }
首先是初始化所有模块的mian_conf,然后合并那些分散的srv_conf
  /* create location trees */ 
//创建location树
    for (s = 0; s < cmcf->servers.nelts; s++) {

        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];  //首先找到当前server块所创建的ngx_http_conf_ctx_t,然后再在里面找到ngx_http_core_module模块的loc_conf

        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;
        }
    }


    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {  //因为nginx把http分化成为11个phase,每一个phase都可以注册handler,ngx_http_core_main_conf_t里面的phases所有的phase handler数组,ngx_http_init_phases函数完成每一个phase的handler的初始化
        return NGX_CONF_ERROR;
    }

    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {// 将http请求的header初始化成hash结构,定义在ngx_http_request.c中的ngx_http_headers_in数组存储了所有的header。这里是根据这个数组去初始化hash结构。
        return NGX_CONF_ERROR;
    }

//遍历所有的http模块,然后调用他们的postconfiguration函数
    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

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

    if (ngx_http_variables_init_vars(cf) != NGX_OK) { //将所有被索引的nginx变量组织成为hash结构
        return NGX_CONF_ERROR;
    }

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

    *cf = pcf;  //恢复配置文件解析结构


    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {   //初始化phase handler
        return NGX_CONF_ERROR;
    }


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

    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {   // 这里开始处理监听方面的初始化了,例如将cycle中的ngx_listening_t的handler设置为ngx_http_init_connection函数,嗯,这个我们在处理accept事件的时候提到过,相当于这里与event部分联系上了
        return NGX_CONF_ERROR;
    }
接下来的代码还是进行一些初始化,最后还要调用ngx_http_optimize_servers来处理http监听,这里就能够与前面讲过的event部分联系上来了。嗯,具体的以后再讲吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值