前言

nginx配置解析是在初始化ngx_cycle_t数据结构时,首先解析core模块,然后core模块依次解析自己的子模块。

配置解析过程

nginx调用ngx_conf_parse函数进行配置文件解析,下面是核心代码,函数首先打开配置文件,然后循环调用ngx_conf_read_token读取一行配置,解析出来保存在cf->args中,如果遇到";" 或者 "{", 在文件没有结束和错误情况下,则会回到ngx_conf_parse中,继续执行ngx_conf_handler函数。

char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename )
{
char *rv;
ngx_fd_t fd ;
ngx_int_t rc ;
ngx_buf_t buf ;
ngx_conf_file_t *prev, conf_file;
enum {
parse_file = 0 ,
parse_block ,
parse_param
} type ; if (filename) {
/* 打开配置文件 */
fd = ngx_open_file(filename->data , NGX_FILE_RDONLY, NGX_FILE_OPEN, 0 );
if ( fd == NGX_INVALID_FILE ) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, ngx_errno ,
ngx_open_file_n " \"%s\" failed",
filename ->data);
return NGX_CONF_ERROR;
} prev = cf-> conf_file; cf ->conf_file = &conf_file ; if ( ngx_fd_info(fd , & cf->conf_file->file .info) == NGX_FILE_ERROR) {
ngx_log_error (NGX_LOG_EMERG, cf->log , ngx_errno,
ngx_fd_info_n " \"%s\" failed", filename->data);
} cf ->conf_file->buffer = &buf; buf .start = ngx_alloc(NGX_CONF_BUFFER , cf-> log);
if ( buf.start == NULL ) {
goto failed;
} buf .pos = buf.start ;
buf .last = buf.start ;
buf .end = buf.last + NGX_CONF_BUFFER;
buf .temporary = 1; cf ->conf_file->file.fd = fd;
cf ->conf_file->file.name .len = filename->len ;
cf ->conf_file->file.name .data = filename->data ;
cf ->conf_file->file.offset = 0 ;
cf ->conf_file->file.log = cf-> log;
cf ->conf_file->line = 1; type = parse_file; } else if (cf->conf_file ->file. fd != NGX_INVALID_FILE ) { type = parse_block; } else {
type = parse_param;
} for ( ;; ) {
rc = ngx_conf_read_token (cf); /*
* ngx_conf_read_token() may return
*
* NGX_ERROR there is error
* NGX_OK the token terminated by ";" was found
* NGX_CONF_BLOCK_START the token terminated by "{" was found
* NGX_CONF_BLOCK_DONE the "}" was found
* NGX_CONF_FILE_DONE the configuration file is done
*/ if (rc == NGX_ERROR) {
goto done;
} if (rc == NGX_CONF_BLOCK_DONE) { if ( type != parse_block ) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
goto failed;
} goto done;
} if (rc == NGX_CONF_FILE_DONE) { if ( type == parse_block ) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"unexpected end of file, expecting \"}\"");
goto failed;
} goto done;
} if (rc == NGX_CONF_BLOCK_START) { if ( type == parse_param ) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"block directives are not supported "
"in -g option");
goto failed;
}
} /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */ if (cf-> handler) { /*
* the custom handler, i.e., that is used in the http's
* "types { ... }" directive
*/ if ( rc == NGX_CONF_BLOCK_START ) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "unexpected \"{\"");
goto failed;
} rv = (* cf->handler)(cf , NULL , cf-> handler_conf);
if ( rv == NGX_CONF_OK ) {
continue;
} if ( rv == NGX_CONF_ERROR ) {
goto failed;
} ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, rv ); goto failed;
} rc = ngx_conf_handler (cf, rc); if (rc == NGX_ERROR) {
goto failed;
}
} failed: rc = NGX_ERROR ; done: if (filename) {
if ( cf->conf_file->buffer ->start) {
ngx_free (cf-> conf_file->buffer ->start);
} if ( ngx_close_file(fd ) == NGX_FILE_ERROR) {
ngx_log_error (NGX_LOG_ALERT, cf->log , ngx_errno,
ngx_close_file_n " %s failed",
filename ->data);
return NGX_CONF_ERROR;
} cf ->conf_file = prev;
} if (rc == NGX_ERROR) {
return NGX_CONF_ERROR;
} return NGX_CONF_OK ;
}

ngx_conf_handler函数如下,函数会遍历所有模块,并匹配配置命令key,取出配置上下文指针,执行相应的set函数。

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
char *rv;
void *conf, **confp;
ngx_uint_t i , found;
ngx_str_t *name;
ngx_command_t *cmd; name = cf ->args-> elts; found = 0; /*遍历所有模块,并比较模块中所有配置的key值*/
for (i = 0 ; ngx_modules[i]; i ++) { cmd = ngx_modules[i]->commands ;
if ( cmd == NULL) {
continue;
} for ( /* void */ ; cmd->name .len; cmd++) { if ( name->len != cmd-> name.len ) {
continue;
}
/*比较命令key值是否和配置的一致*/
if ( ngx_strcmp(name ->data, cmd->name .data) != 0) {
continue;
} found = 1 ; /*过滤掉非指定模块,进入ngx_conf_parse前,会指定module_type值,解析指定模块*/
if ( ngx_modules[i ]->type != NGX_CONF_MODULE
&& ngx_modules[i]->type != cf-> module_type)
{
continue;
} /*判断解析的配置命令类型是否和指定的类型一致,cmd_type在进入ngx_conf_parse前指定*/ if (!( cmd->type & cf-> cmd_type)) {
continue;
} if (!( cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"directive \"%s\" is not terminated by \";\"",
name ->data);
return NGX_ERROR;
} if (( cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"directive \"%s\" has no opening \"{\"",
name ->data);
return NGX_ERROR;
} /*判断配置参数个数是否正确*/ if (!( cmd->type & NGX_CONF_ANY)) { if ( cmd->type & NGX_CONF_FLAG) { if ( cf->args->nelts != 2 ) {
goto invalid;
} } else if (cmd ->type & NGX_CONF_1MORE) { if ( cf->args->nelts < 2 ) {
goto invalid;
} } else if (cmd ->type & NGX_CONF_2MORE) { if ( cf->args->nelts < 3 ) {
goto invalid;
} } else if (cf ->args-> nelts > NGX_CONF_MAX_ARGS ) { goto invalid; } else if (!(cmd ->type & argument_number[cf ->args-> nelts - 1 ]))
{
goto invalid;
}
} /* 获取指定配置上下文的指针,*/ conf = NULL ; if ( cmd->type & NGX_DIRECT_CONF) {
/*
*取ngx_core_module模块配置ngx_core_conf_t指针,由于该模块结构中直接配置了create_conf回调函数,已经分配过内存,直接使用
*/
conf = (( void **) cf->ctx )[ngx_modules[i]->index ]; } else if (cmd ->type & NGX_MAIN_CONF) {
/*
*假设cmd->name值是http,即正在解析http{}块,(((void **) cf->ctx)[ngx_modules[i]->index]取出的是ngx_http_module模块配置ngx_http_conf_ctx_t的指针,
*由于配置还未进行内存分配,因此这里取了指针的地址,以备后面ngx_http_block对其进行内存分配
*/
conf = &((( void **) cf->ctx )[ngx_modules[i]->index ]); } else if (cf ->ctx) {
/*
*假设cmd->name值是location,并出现在server{}第一层中,即解析server { location{} }块,首先confp取出的是ngx_http_conf_ctx_t中srv_conf指针值,
*conf则取出当前模块对应的http配置指针
*/
confp = *( void **) ((char *) cf->ctx + cmd-> conf); if ( confp) {
conf = confp[ ngx_modules[i ]->ctx_index];
}
} /*配置的set函数*/
rv = cmd-> set(cf , cmd, conf); if ( rv == NGX_CONF_OK ) {
return NGX_OK;
} if ( rv == NGX_CONF_ERROR ) {
return NGX_ERROR;
} ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"\"%s\" directive %s" , name-> data, rv ); return NGX_ERROR;
}
} if (found) {
ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"\"%s\" directive is not allowed here" , name->data); return NGX_ERROR;
} ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"unknown directive \"%s\"" , name-> data); return NGX_ERROR ; invalid: ngx_conf_log_error (NGX_LOG_EMERG, cf, 0,
"invalid number of arguments in \"%s\" directive",
name ->data); return NGX_ERROR ;
}

下面我们假设配置读取的是http{}块,则set函数即为ngx_http_block,函数首先给ngx_http_conf_ctx_t配置分配内存,然后递归调用ngx_conf_parse开始解析所有NGX_HTTP_MODULE模块中main_conf字段,解析中如果遇到location{}或者server{}块,则调用相应的set函数进行解析,整个http{}块共享同一个main_conf。如果一个字段可以同时出现在main_conf,srv_conf中,则这个字段会保存在srv_conf中,如果一个字段可以同时出现在main_conf,srv_conf,loc_conf中,则此配置必定保存在loc_conf中。配置的合并其实就是对那些可以出现在多个层的字段,如果内层没有赋值,则由外层向内层赋值过程。

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; /*ngx_http_ctx_t分配内存*/
ctx = ngx_pcalloc (cf-> pool, sizeof(ngx_http_conf_ctx_t ));
if (ctx == NULL) {
return NGX_CONF_ERROR;
} /*conf指针即ngx_conf_handler中取出的ngx_http_ctx_t指针的地址,将分配的内存赋值,相当于(((void **) cf->ctx)[ngx_modules[i]->index]) = ctx*/
*(ngx_http_conf_ctx_t **) conf = ctx; /*设置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 ++;
} /* main_conf整个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;
} /*
* http{}块没有srv_conf,合并时使用
*/
ctx->srv_conf = ngx_pcalloc(cf->pool , sizeof (void *) * ngx_http_max_module);
if (ctx-> srv_conf == NULL) {
return NGX_CONF_ERROR;
} /*
* http{}没有loc_conf,合并时使用
*/ ctx->loc_conf = ngx_pcalloc(cf->pool , sizeof (void *) * ngx_http_max_module);
if (ctx-> loc_conf == NULL) {
return NGX_CONF_ERROR;
} /*
* create the main_conf's, the null srv_conf's, and the null loc_conf's
* of the all http modules
*/ 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 ; if ( module->create_main_conf ) {
ctx ->main_conf[mi] = module->create_main_conf(cf );
if ( ctx->main_conf [mi] == NULL) {
return NGX_CONF_ERROR;
}
} if ( module->create_srv_conf ) {
ctx ->srv_conf[mi] = module->create_srv_conf(cf );
if ( ctx->srv_conf [mi] == NULL) {
return NGX_CONF_ERROR;
}
} if ( module->create_loc_conf ) {
ctx ->loc_conf[mi] = module->create_loc_conf(cf );
if ( ctx->loc_conf [mi] == NULL) {
return NGX_CONF_ERROR;
}
}
} /*
*保存原来上下文配置,cf->ctx赋值为ngx_http_ctx_t指针,后面要开始递归解析http{}块,在后面的我们会看到很多这样的操作
*/
pcf = *cf;
cf->ctx = ctx; for (m = 0 ; ngx_modules[m]; m ++) {
if ( ngx_modules[m ]->type != NGX_HTTP_MODULE) {
continue;
} module = ngx_modules[m]->ctx ; if ( module->preconfiguration ) {
if ( module->preconfiguration (cf) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} /*
*module_type和cmd_type赋值,进入ngx_conf_parse后解析NGX_HTTP_MODULE模块,并且NGX_HTTP_MAIN_CONF类型的配置
*/ cf->module_type = NGX_HTTP_MODULE;
cf->cmd_type = NGX_HTTP_MAIN_CONF;
rv = ngx_conf_parse (cf, NULL); if (rv != NGX_CONF_OK) {
goto failed;
} /*
* 上面解析完成后,所有的server{}列表保存在cmcf->servers中,然后开始合并server{}块,合并server{}块时,同时会开始合并location{}块
*/
cmcf = ctx ->main_conf[ngx_http_core_module.ctx_index ];
cscfp = cmcf ->servers.elts; 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 ]);
if ( rv != NGX_CONF_OK ) {
goto failed;
}
} rv = ngx_http_merge_servers(cf, cmcf , module, mi);
if ( rv != NGX_CONF_OK ) {
goto failed;
}
} /* 创建location节点树, 把location链表转换成三叉树, 利于用户根据请求url取出相应的配置 */ 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;
}
} 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;
} 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) {
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) {
return NGX_CONF_ERROR;
} /* optimize the lists of ports, addresses and server names */ if (ngx_http_optimize_servers(cf, cmcf , cmcf-> ports) != NGX_OK) {
return NGX_CONF_ERROR;
} return NGX_CONF_OK ; failed: *cf = pcf; return rv ;
}

http模块解析完成后, 所有的server配置都会保存在cmcf->servers数组中, 每个server下的location会组成一个三叉树, 保存在相对应的server中ngx_http_core_loc_conf_t中static_locations. 服务器接收一个url请求后, 通过相应的地址和端口找到server配置, 通过三叉树找到相匹配的ngx_http_core_loc_conf_t配置, 进而获取到正确的配置信息.

NGINX(四)配置解析的更多相关文章

  1. nginx.conf 配置解析之文件结构

    nginx.conf配置文件结构如下: ...... #主要定义nginx的全局配置 events{ #events(事件)块:主要配置网络连接相关 } http{ #http块:代理缓存和日志定义绝 ...

  2. nginx.conf 配置解析之 server配置

    server{} 包含在http{}内部,每一个server{}都是一个虚拟主机(站点) 以下为nginx.conf配置文件中server{  }部分的内容. server { listen ; // ...

  3. nginx.conf 配置解析之 http配置

    官方文档 http://nginx.org/en/docs/参考链接: https://segmentfault.com/a/1190000012672431参考链接: https://segment ...

  4. nginx.conf 配置解析之 全局配置

    user nobody; 定义运行nginx服务的用户,还可以加上组,如 user nobody nobody; worker_processes 1; 定义nginx子进程数量,即提供服务的进程数量 ...

  5. nginx常用配置解析

    1.常用公共参数(一般放在http下面,虽然很多参数都支持server和location) keepalive_timeout  60;  #单位为s keepalive_request 2;  #设 ...

  6. Nginx 默认配置解析

    # For more information on configuration, see: # * Official English Documentation: http://nginx.org/e ...

  7. nginx.conf 配置解析之 events配置

    worker_connections 1024; 定义每个work_process同时开启的最大连接数,即允许最多只能有这么多连接. accept_mutex on; 当某一个时刻只有一个网络连接请求 ...

  8. 初识nginx——配置解析篇

    一.nginx的介绍 nginx是由俄罗斯人开发的一款高性能的http和反向代理服务器,也可以用来作为邮件代理.相比较于其他的服务器,具有占用内存少,稳定性高等优势 二.nginx的配置 nginx的 ...

  9. Nginx(六):配置解析之location解析

    nginx成为非常流行的代理服务软件,最根本的原因也许是在于其强悍性能.但还有一些必要的条件,比如功能的完整,配置的易用,能够解决各种各样的实际需求问题,这些是一个好的软件的必备特性. 那么,今天我们 ...

随机推荐

  1. Js 处理将时间转换 “年-月-日”

    将时间  \/Date(1432828800000+0800)\/"  转换成:“年-月-日” //时间转换function ChangeDateFormat(val) {    if (v ...

  2. C# Windows - RadioButton&CheckBox

    RadioButton和CheckBox控件与Button控件有相同的基类,但它们的外观和用法大不相同. RadioButton显示为一个标签,左边是一个圆点,该点可以是选中或未选中.用在给用户提供两 ...

  3. android/IOS各平台分享链接/跳转链接配置说明(备用)

    Android: [Java] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 微信/朋友圈 //设置 ...

  4. 简单3d RPG游戏 之 004 攻击(一)

    功能:实现点击键盘F键,怪物血量条减少,并且假定是近战,需要对距离进行判断,距离小于一定值的时候按F才会减少怪物的血条. 新建c#脚本PlayerAttack,绑定到Player,并在unity里将敌 ...

  5. 小小地预览HTML5

    程序示例 <!doctype html> <html> <head> <title>First </title> <meta char ...

  6. 实时数据处理环境搭建flume+kafka+storm:3.kafka安装

    1.  解压  tar -zxvf   2.配置/app/kafka_2.9.2-0.8.1.1/config/server.properties     #标识--     broker.id=0 ...

  7. uc/os初始化

        操作系统初始化函数OS_INIT是操作系统在开始运行的最初,对全局变量.任务控制块.就绪表.事件及消息队列等重要数据结构进行的初始化操作,并创建空闲任务.统计任务等系统任务.该函数必须在创建用 ...

  8. Hybrid App 和 React Native 开发那点事

    简介:Hybrid App(混合模式移动应用)开发是指介于Web-app.Native-App这两者之间的一种开发模式,兼具「Native App 良好用户交互体验的优势」和「Web App 跨平台开 ...

  9. hbase命令备忘

    http://www.cnblogs.com/linjiqin/archive/2013/03/08/2949339.html HBase 为用户提供了一个非常方便的使用方式, 我们称之为“HBase ...

  10. PowerDesigner(五)-概念数据模型(CDM生成LDM,PDM和OOM)

    概念数据模型 概念数据模型(Conceptual Data Model,CDM):表达的是数据整体逻辑结构,该结构独立于任何软件和数据存储结构,即它只是系统分析人员,应用程序设计人员,维护人员和用户之 ...