Nginx configuration analysis
Overview
In the previous article " Nginx Startup Initialization Process" briefly introduces the startup process of Nginx and analyzes the source code of its startup process. There is a very important step in the startup process, which is to call the function ngx_init_cycle(). The call of this function provides an interface for configuration analysis. Configuration analysis interface can be divided into two stages: preparing data phase and configuration analysis stage; The parsing phase is to call the function:
/* 配置文件解析 */ if (ngx_conf_param(&conf) != NGX_CONF_OK) {/* 带有命令行参数'-g' 加入的配置 */ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; } if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {/* 解析配置文件*/ environ = senv; ngx_destroy_cycle_pools(&conf); return NULL; }
Configuration parsing
- ngx_conf_t Structure
- This structure is used by
- Nginx to describe the attributes of each directive when parsing the configuration file. It is also a very important one in the
program. Data structure, which is defined in the file:
src/core/ngx_conf_file.h/* 解析配置时所使用的结构体 */ struct ngx_conf_s { char *name; /* 当前解析到的指令 */ ngx_array_t *args; /* 当前指令所包含的所有参数 */ ngx_cycle_t *cycle; /* 待解析的全局变量ngx_cycle_t */ ngx_pool_t *pool; /* 内存池 */ ngx_pool_t *temp_pool;/* 临时内存池,分配一些临时数组或变量 */ ngx_conf_file_t *conf_file;/* 待解析的配置文件 */ ngx_log_t *log; /* 日志信息 */ void *ctx; /* 描述指令的上下文 */ ngx_uint_t module_type;/* 当前解析的指令的模块类型 */ ngx_uint_t cmd_type; /* 当前解析的指令的指令类型 */ ngx_conf_handler_pt handler; /* 模块自定义的handler,即指令自定义的处理函数 */ char *handler_conf;/* 自定义处理函数需要的相关配置 */ };
conf_file is to store the relevant information of the Nginx configuration file. The definition of the ngx_conf_file_t
structure is as follows:
typedef struct { ngx_file_t file; /* 文件的属性 */ ngx_buf_t *buffer; /* 文件的内容 */ ngx_uint_t line; /* 文件的行数 */ } ngx_conf_file_t;
Configuration contextctx
Nginx configuration files are configured in blocks. Common ones include http block, server block,
locationblock and upsteam block and
mailblock among others. Each such configuration block represents a scope. The scope of a higher-level configuration block contains the scopes of multiple lower-level configuration blocks, which is the phenomenon of scope nesting. In this way, many directives in the configuration file will be included in multiple scopes at the same time. For example, the instructions in the http block may be in three scopes at the same time: http block, server block and location block. When the Nginx program parses the configuration file, each instruction should record the scope to which it belongs, and the configuration file context ctx variable is used to store the scope to which the current instruction belongs. Among the various configuration blocks of the Nginx configuration file, the http block can contain sub-configuration blocks, which is more complex in terms of storage structure. Different instruction types in the instructions pType
Nginx Program are defined in different source dock files in the form of macro. ngx_conf_file.h
#define NGX_DIRECT_CONF 0x00010000 #define NGX_MAIN_CONF 0x01000000 #define NGX_ANY_CONF 0x0F000000
program enters the configuration parsing function, so after entering the configuration parsing function, they can be directly parsed and stored in the actual data structure. From the structure of the configuration file, They generally refer to those instructions that are outside the configuration block and are in the global block part of the configuration file. NGX_MAIN_CONF class instructions include
event, http, mail, upstream and other instructions that can form configuration blocks. They do not have their own initialization function. If the
Nginx program encounters a NGX_MAIN_CONF class instruction when parsing the configuration file, it will transfer to the parsing of the next level instruction. The following are the instruction types supported by the event type module.#define NGX_EVENT_CONF 0x02000000
#define NGX_HTTP_MAIN_CONF 0x02000000 #define NGX_HTTP_SRV_CONF 0x04000000 #define NGX_HTTP_LOC_CONF 0x08000000 #define NGX_HTTP_UPS_CONF 0x10000000 #define NGX_HTTP_SIF_CONF 0x20000000 #define NGX_HTTP_LIF_CONF 0x40000000 #define NGX_HTTP_LMT_CONF 0x80000000
ngx_conf_param, which is used to parse the configuration passed from the command line. This interface is also a wrapper for ngx_conf_parse
. First, let’s take a look at the configuration parsing functionngx_conf_parse, which is defined as follows:
/* * 函数功能:配置文件解析; * 支持三种不同的解析类型: * 1、解析配置文件; * 2、解析block块设置; * 3、解析命令行配置; */ 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 (NGX_SUPPRESS_WARN) fd = NGX_INVALID_FILE; prev = NULL; #endif if (filename) {/* 若解析的是配置文件 */ /* open configuration file */ /* 打开配置文件 */ 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; /* 解析的类型是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; } /* 解析block块设置 */ 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; } /* 若自定义指令处理函数handler为NULL,则调用Nginx内建的指令解析机制 */ 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; }
’s built-in command parsing mechanism; the other is a custom command parsing mechanism. The custom instruction parsing source code is as follows:
/* 自定义指令处理函数 */ 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; }
’s built-in parsing mechanism is implemented by the function ngx_conf_handler(). Its definition is as follows:
/* Nginx内建的指令解析机制 */ 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; 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; } if (ngx_strcmp(name->data, cmd->name.data) != 0) { continue; } found = 1; /* * 只处理模块类型为NGX_CONF_MODULE 或是当前正在处理的模块类型; */ if (ngx_modules[i]->type != NGX_CONF_MODULE && ngx_modules[i]->type != cf->module_type) { continue; } /* is the directive's location right ? */ if (!(cmd->type & cf->cmd_type)) { continue; } /* 非block块指令必须以";"分号结尾,否则出错返回 */ 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; } /* block块指令必须后接"{"大括号,否则出粗返回 */ 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; } /* is the directive's argument count right ? */ /* 验证指令参数个数是否正确 */ if (!(cmd->type & NGX_CONF_ANY)) { /* 指令携带的参数只能是 1 个,且其参数值只能是 on 或 off */ if (cmd->type & NGX_CONF_FLAG) { if (cf->args->nelts != 2) { goto invalid; } } else if (cmd->type & NGX_CONF_1MORE) {/* 指令携带的参数必须超过 1 个 */ if (cf->args->nelts < 2) { goto invalid; } } else if (cmd->type & NGX_CONF_2MORE) {/* 指令携带的参数必须超过 2 个 */ 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; } } /* set up the directive's configuration context */ conf = NULL; if (cmd->type & NGX_DIRECT_CONF) {/* 在core模块使用 */ conf = ((void **) cf->ctx)[ngx_modules[i]->index]; } else if (cmd->type & NGX_MAIN_CONF) {/* 指令配置项出现在全局配置中,不属于任何{}配置块 */ conf = &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) {/* 除了core模块,其他模块都是用该项 */ confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } /* 执行指令解析回调函数 */ 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; }
struct ngx_command_s { /* 配置项名称 */ ngx_str_t name; /* 配置项类型,type将指定配置项可以出现的位置以及携带参数的个数 */ ngx_uint_t type; /* 处理配置项的参数 */ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); /* 在配置文件中的偏移量,conf与offset配合使用 */ ngx_uint_t conf; ngx_uint_t offset; /* 配置项读取后的处理方法,必须指向ngx_conf_post_t 结构 */ void *post; };
若在上面的通用配置解析中,定义了如下的 http 配置项结构,则回调用http 配置项,并对该http 配置项进行解析。此时,解析的是http block 块设置。
static ngx_command_t ngx_http_commands[] = { { ngx_string("http"), NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, ngx_http_block, 0, 0, NULL }, ngx_null_command };
http 是作为一个 core 模块被 nginx 通用解析过程解析的,其核心就是http{} 块指令回调,它完成了http 解析的整个功能,从初始化到计算配置结果。http{} 块指令的流程是:
- 创建并初始化上下文结构;
- 调用通用模块配置解析流程解析;
- 根据解析结果进行配置项合并处理;
创建并初始化上下文结构
当 Nginx 检查到 http{…} 配置项时,HTTP 配置模型就会启动,则会建立一个ngx_http_conf_ctx_t 结构,该结构定义在文件中:src/http/ngx_http_config.h
typedef struct{ /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_main_conf 方法产生的结构体 */ void **main_conf; /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_srv_conf 方法产生的结构体 */ void **srv_conf; /* 指针数组,数组中的每个元素指向所有 HTTP 模块 create_loc_conf 方法产生的结构体 */ void **loc_conf; }ngx_http_conf_ctx_t;
此时,HTTP 框架为所有 HTTP 模块建立 3 个数组,分别存放所有 HTTP 模块的create_main_conf、create_srv_conf 、create_loc_conf 方法返回的地址指针。ngx_http_conf_ctx_t 结构的三个成员分别指向这 3 个数组。例如下面的例子是设置 create_main_conf、create_srv_conf 、create_loc_conf 返回的地址。
ngx_http_conf_ctx *ctx; /* HTTP 框架生成 1 个 ngx_http_conf_ctx_t 结构变量 */ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t)); *(ngx_http_conf_ctx_t **) conf = ctx; ... /* 分别生成 3 个数组存储所有的 HTTP 模块的 create_main_conf、create_srv_conf、create_loc_conf 方法返回的地址 */ 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); /* 遍历所有 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; /* 若实现了create_main_conf 方法,则调用该方法,并把返回的地址存储到 main_conf 中 */ if (module->create_main_conf) { ctx->main_conf[mi] = module->create_main_conf(cf); } /* 若实现了create_srv_conf 方法,则调用该方法,并把返回的地址存储到 srv_conf 中 */ if (module->create_srv_conf) { ctx->srv_conf[mi] = module->create_srv_conf(cf); } /* 若实现了create_loc_conf 方法,则调用该方法,并把返回的地址存储到 loc_conf 中 */ if (module->create_loc_conf) { ctx->loc_conf[mi] = module->create_loc_conf(cf); } } 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; } } }
调用通用模块配置解析流程解析
从源码 src/http/ngx_http.c 中可以看到,http 块的配置解析是调用通用模块的配置解析函数,其实现如下:
/* 调用通用模块配置解析 */ /* parse inside the http{} block */ 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; }
根据解析结果进行配置项合并处理
/* 根据解析结构进行合并处理 */ /* * 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]; 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; } } /* create location trees */ 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 配置解析流程
从上面的分析中可以总结出 HTTP 配置解析的流程如下:
- Nginx 进程进入主循环,在主循环中调用配置解析器解析配置文件nginx.conf;
- 在配置文件中遇到 http{} 块配置,则 HTTP 框架开始启动,其由函数 ngx_http_block() 实现;
- HTTP 框架初始化所有 HTTP 模块的序列号,并创建 3 个类型为 ngx_http_conf_ctx_t 结构的数组用于存储所有HTTP 模块的create_main_conf、create_srv_conf、create_loc_conf方法返回的指针地址;
- 调用每个 HTTP 模块的 preconfiguration 方法;
- HTTP 框架调用函数 ngx_conf_parse() 开始循环解析配置文件 nginx.conf 中的http{}块里面的所有配置项;
- HTTP 框架处理完毕 http{} 配置项,根据解析配置项的结果,必要时进行配置项合并处理;
- 继续处理其他 http{} 块之外的配置项,直到配置文件解析器处理完所有配置项后通知Nginx 主循环配置项解析完毕。此时,Nginx 才会启动Web 服务器;
合并配置项
HTTP 框架解析完毕 http{} 块配置项时,会根据解析的结果进行合并配置项操作,即合并 http{}、server{}、location{} 不同块下各HTTP 模块生成的存放配置项的结构体。其合并过程如下所示:
- 若 HTTP 模块实现了 merge_srv_conf 方法,则将 http{} 块下create_srv_conf 生成的结构体与遍历每一个 server{}配置块下的结构体进行merge_srv_conf 操作;
- 若 HTTP 模块实现了 merge_loc_conf 方法,则将 http{} 块下create_loc_conf 生成的结构体与嵌套每一个server{} 配置块下的结构体进行merge_loc_conf 操作;
- 若 HTTP 模块实现了 merge_loc_conf 方法,则将server{} 块下create_loc_conf 生成的结构体与嵌套每一个location{}配置块下的结构体进行merge_loc_conf 操作;
- 若 HTTP 模块实现了 merge_loc_conf 方法,则将location{} 块下create_loc_conf 生成的结构体与嵌套每一个location{}配置块下的结构体进行merge_loc_conf 操作;
以下是合并配置项操作的源码实现:
/* 合并配置项操作 */ static char * ngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_uint_t s; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t **cscfp; cscfp = cmcf->servers.elts; ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; rv = NGX_CONF_OK; /* 遍历每一个server{}块 */ for (s = 0; s < cmcf->servers.nelts; s++) { /* merge the server{}s' srv_conf's */ ctx->srv_conf = cscfp[s]->ctx->srv_conf; /* * 若定义了merge_srv_conf 方法; * 则进行http{}块下create_srv_conf 生成的结构体与遍历server{}块配置项生成的结构体进行merge_srv_conf操作; */ if (module->merge_srv_conf) { rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } } /* * 若定义了merge_loc_conf 方法; * 则进行http{}块下create_loc_conf 生成的结构体与嵌套server{}块配置项生成的结构体进行merge_loc_conf操作; */ if (module->merge_loc_conf) { /* merge the server{}'s loc_conf */ ctx->loc_conf = cscfp[s]->ctx->loc_conf; rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { goto failed; } /* merge the locations{}' loc_conf's */ /* * 若定义了merge_loc_conf 方法; * 则进行server{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作; */ clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; rv = ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { goto failed; } } } failed: *ctx = saved; return rv; } static char * ngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char *rv; ngx_queue_t *q; ngx_http_conf_ctx_t *ctx, saved; ngx_http_core_loc_conf_t *clcf; ngx_http_location_queue_t *lq; if (locations == NULL) { return NGX_CONF_OK; } ctx = (ngx_http_conf_ctx_t *) cf->ctx; saved = *ctx; /* * 若定义了merge_loc_conf 方法; * 则进行location{}块下create_loc_conf 生成的结构体与嵌套location{}块配置项生成的结构体进行merge_loc_conf操作; */ for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; ctx->loc_conf = clcf->loc_conf; rv = module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]); if (rv != NGX_CONF_OK) { return rv; } /* * 递归调用该函数; * 因为location{}继续内嵌location{} */ rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module, ctx_index); if (rv != NGX_CONF_OK) { return rv; } } *ctx = saved; return NGX_CONF_OK; }
处理自定义的配置
在文章中 《Nginx 模块开发》,我们给出了“Hello World” 的开发例子,在这个开发例子中,我们定义了自己的配置项,配置项名称的结构体定义如下:
typedef struct { ngx_str_t hello_string; ngx_int_t hello_counter; }ngx_http_hello_loc_conf_t;
为了处理我们定义的配置项结构,因此,我们把 ngx_command_t 结构体定义如下:
static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, ngx_http_hello_string, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_string), NULL }, { ngx_string("hello_counter"), NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_hello_counter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_counter), NULL }, ngx_null_command };
处理方法 ngx_http_hello_string 和ngx_http_hello_counter 定义如下:
static char * ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = ngx_conf_set_str_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data); return rv; } static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = NULL; rv = ngx_conf_set_flag_slot(cf, cmd, conf); ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter); return rv; }
参考资料:
《深入理解 Nginx 》
《nginx 启动阶段》
《Nginx高性能Web服务器详解》
以上就介绍了Nginx 配置解析,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics



The message "Your organization has asked you to change your PIN" will appear on the login screen. This happens when the PIN expiration limit is reached on a computer using organization-based account settings, where they have control over personal devices. However, if you set up Windows using a personal account, the error message should ideally not appear. Although this is not always the case. Most users who encounter errors report using their personal accounts. Why does my organization ask me to change my PIN on Windows 11? It's possible that your account is associated with an organization, and your primary approach should be to verify this. Contacting your domain administrator can help! Additionally, misconfigured local policy settings or incorrect registry keys can cause errors. Right now

Windows 11 brings fresh and elegant design to the forefront; the modern interface allows you to personalize and change the finest details, such as window borders. In this guide, we'll discuss step-by-step instructions to help you create an environment that reflects your style in the Windows operating system. How to change window border settings? Press + to open the Settings app. WindowsI go to Personalization and click Color Settings. Color Change Window Borders Settings Window 11" Width="643" Height="500" > Find the Show accent color on title bar and window borders option, and toggle the switch next to it. To display accent colors on the Start menu and taskbar To display the theme color on the Start menu and taskbar, turn on Show theme on the Start menu and taskbar

We all have different preferences when it comes to display scaling on Windows 11. Some people like big icons, some like small icons. However, we all agree that having the right scaling is important. Poor font scaling or over-scaling of images can be a real productivity killer when working, so you need to know how to customize it to get the most out of your system's capabilities. Advantages of Custom Zoom: This is a useful feature for people who have difficulty reading text on the screen. It helps you see more on the screen at one time. You can create custom extension profiles that apply only to certain monitors and applications. Can help improve the performance of low-end hardware. It gives you more control over what's on your screen. How to use Windows 11

Many users will choose the Huawei brand when choosing smart watches. Among them, Huawei GT3pro and GT4 are very popular choices. Many users are curious about the difference between Huawei GT3pro and GT4. Let’s introduce the two to you. . What are the differences between Huawei GT3pro and GT4? 1. Appearance GT4: 46mm and 41mm, the material is glass mirror + stainless steel body + high-resolution fiber back shell. GT3pro: 46.6mm and 42.9mm, the material is sapphire glass + titanium body/ceramic body + ceramic back shell 2. Healthy GT4: Using the latest Huawei Truseen5.5+ algorithm, the results will be more accurate. GT3pro: Added ECG electrocardiogram and blood vessel and safety

Screen brightness is an integral part of using modern computing devices, especially when you look at the screen for long periods of time. It helps you reduce eye strain, improve legibility, and view content easily and efficiently. However, depending on your settings, it can sometimes be difficult to manage brightness, especially on Windows 11 with the new UI changes. If you're having trouble adjusting brightness, here are all the ways to manage brightness on Windows 11. How to Change Brightness on Windows 11 [10 Ways Explained] Single monitor users can use the following methods to adjust brightness on Windows 11. This includes desktop systems using a single monitor as well as laptops. let's start. Method 1: Use the Action Center The Action Center is accessible

HTTP status code 520 means that the server encountered an unknown error while processing the request and cannot provide more specific information. Used to indicate that an unknown error occurred when the server was processing the request, which may be caused by server configuration problems, network problems, or other unknown reasons. This is usually caused by server configuration issues, network issues, server overload, or coding errors. If you encounter a status code 520 error, it is best to contact the website administrator or technical support team for more information and assistance.

In iOS 17, Apple introduced several new privacy and security features to its mobile operating system, one of which is the ability to require two-step authentication for private browsing tabs in Safari. Here's how it works and how to turn it off. On an iPhone or iPad running iOS 17 or iPadOS 17, Apple's browser now requires Face ID/Touch ID authentication or a passcode if you have any Private Browsing tab open in Safari and then exit the session or app to access them again. In other words, if someone gets their hands on your iPhone or iPad while it's unlocked, they still won't be able to view your privacy without knowing your passcode

The famous activation script MAS2.2 version supports digital activation again. The method originated from @asdcorp and the team. The MAS author calls it HWID2. Download gatherosstate.exe (not original, modified) from https://github.com/massgravel/Microsoft-Activation-Scripts, run it with parameters, and generate GenuineTicket.xml. First take a look at the original method: gatherosstate.exePfn=xxxxxxx;DownlevelGenuineState=1 and then compare with the latest method: gatheros
