NGINX(四)配置解析
前言
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(四)配置解析的更多相关文章
- nginx.conf 配置解析之文件结构
nginx.conf配置文件结构如下: ...... #主要定义nginx的全局配置 events{ #events(事件)块:主要配置网络连接相关 } http{ #http块:代理缓存和日志定义绝 ...
- nginx.conf 配置解析之 server配置
server{} 包含在http{}内部,每一个server{}都是一个虚拟主机(站点) 以下为nginx.conf配置文件中server{ }部分的内容. server { listen ; // ...
- nginx.conf 配置解析之 http配置
官方文档 http://nginx.org/en/docs/参考链接: https://segmentfault.com/a/1190000012672431参考链接: https://segment ...
- nginx.conf 配置解析之 全局配置
user nobody; 定义运行nginx服务的用户,还可以加上组,如 user nobody nobody; worker_processes 1; 定义nginx子进程数量,即提供服务的进程数量 ...
- nginx常用配置解析
1.常用公共参数(一般放在http下面,虽然很多参数都支持server和location) keepalive_timeout 60; #单位为s keepalive_request 2; #设 ...
- Nginx 默认配置解析
# For more information on configuration, see: # * Official English Documentation: http://nginx.org/e ...
- nginx.conf 配置解析之 events配置
worker_connections 1024; 定义每个work_process同时开启的最大连接数,即允许最多只能有这么多连接. accept_mutex on; 当某一个时刻只有一个网络连接请求 ...
- 初识nginx——配置解析篇
一.nginx的介绍 nginx是由俄罗斯人开发的一款高性能的http和反向代理服务器,也可以用来作为邮件代理.相比较于其他的服务器,具有占用内存少,稳定性高等优势 二.nginx的配置 nginx的 ...
- Nginx(六):配置解析之location解析
nginx成为非常流行的代理服务软件,最根本的原因也许是在于其强悍性能.但还有一些必要的条件,比如功能的完整,配置的易用,能够解决各种各样的实际需求问题,这些是一个好的软件的必备特性. 那么,今天我们 ...
随机推荐
- Js 处理将时间转换 “年-月-日”
将时间 \/Date(1432828800000+0800)\/" 转换成:“年-月-日” //时间转换function ChangeDateFormat(val) { if (v ...
- C# Windows - RadioButton&CheckBox
RadioButton和CheckBox控件与Button控件有相同的基类,但它们的外观和用法大不相同. RadioButton显示为一个标签,左边是一个圆点,该点可以是选中或未选中.用在给用户提供两 ...
- android/IOS各平台分享链接/跳转链接配置说明(备用)
Android: [Java] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 微信/朋友圈 //设置 ...
- 简单3d RPG游戏 之 004 攻击(一)
功能:实现点击键盘F键,怪物血量条减少,并且假定是近战,需要对距离进行判断,距离小于一定值的时候按F才会减少怪物的血条. 新建c#脚本PlayerAttack,绑定到Player,并在unity里将敌 ...
- 小小地预览HTML5
程序示例 <!doctype html> <html> <head> <title>First </title> <meta char ...
- 实时数据处理环境搭建flume+kafka+storm:3.kafka安装
1. 解压 tar -zxvf 2.配置/app/kafka_2.9.2-0.8.1.1/config/server.properties #标识-- broker.id=0 ...
- uc/os初始化
操作系统初始化函数OS_INIT是操作系统在开始运行的最初,对全局变量.任务控制块.就绪表.事件及消息队列等重要数据结构进行的初始化操作,并创建空闲任务.统计任务等系统任务.该函数必须在创建用 ...
- Hybrid App 和 React Native 开发那点事
简介:Hybrid App(混合模式移动应用)开发是指介于Web-app.Native-App这两者之间的一种开发模式,兼具「Native App 良好用户交互体验的优势」和「Web App 跨平台开 ...
- hbase命令备忘
http://www.cnblogs.com/linjiqin/archive/2013/03/08/2949339.html HBase 为用户提供了一个非常方便的使用方式, 我们称之为“HBase ...
- PowerDesigner(五)-概念数据模型(CDM生成LDM,PDM和OOM)
概念数据模型 概念数据模型(Conceptual Data Model,CDM):表达的是数据整体逻辑结构,该结构独立于任何软件和数据存储结构,即它只是系统分析人员,应用程序设计人员,维护人员和用户之 ...