nginx源码分析——配置

故事扮演 提交于 2019-12-04 22:34:44

1. 配置介绍

nginx的配置由一个主配置文件和其他一些辅助的配置文件构成。这些配置文件均是纯文本文件,这些配置文件全部位于nginx安装目录下的conf目录中。

主配置文件nginx.conf中的内容大概是这样子的:

#user nobody;
worker_processes    1;
pid                 logs/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include         mime.types;
    default_type    application/octet-stream;

    sendfile        on;
    keepalive_timeout 65;

    server {
        listen    80;
        server_name localhost;

        location / {
            root    html;
            index   index.html index.htm;
        }

        error_page    500 502 503 504 /50x.html;
        location = /50x.html {
            root    html;
        }
    }
}

从上面可以看出:nginx.conf由若干配置项组成,配置项又分为简单配置项和块配置项。

简单配置项由配置项名和配置项值构成。

配置项名是一个字符串,可以用单引号或者双引号括起来,也可以不扩。但是如果配置项名包含空格,则一定要括起来。配置项值使用一个或者多个空格或者TAB与配置项名分开,配置项值可以是数字或者字符串(当然也包括正则表达式)。一个配置项可以有多个配置项值,多个配置项值之间也由空格或者TAB分隔。简单配置项的结尾使用分号结束,其基本的语法格式如下:

配置项名    配置项值1  配置项值2 ... ;

块配置项由一个配置项名和一对大括号组成,其结尾不需要再添加分号。块配置项使用大括号把一系列所属的配置项全部包含起来,表示大括号内的配置项同时生效;块配置项也可以有配置项值,这取决于解析这个配置项的模块;块配置项可以嵌套,内层块直接继承外层块,同一个配置项可以同时出现在内外层块配置项中,其优先级也取决于解析该配置项的模块。

2. 配置解析流程

nginx配置解析的流程基本上是一个递归的过程,如下图所示:

在配置解析的入口函数 ngx_conf_parse 中会循环读取配置文件中的内容,每读取一行后就解析该配置项。解析的过程中可能会再次调用该函数完成后续配置项的解析,例如events块配置项,http块配置项的解析。

对应的源码:

char * ngx_conf_parse(ngx_conf_t * cf, ngx_str_t * filename)
{
    ...
    if( filename ){
        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
        ...
    } else if(cf->conf_file->file.fd != NGX_INVALID_FILE){
        type = parse_block;
    } else {
        type = parse_param;
    }

    for( ;; ){
        rc = ngx_conf_read_token(cf);

        if(cf->handler){
            ...
        }

        rc = ngx_conf_handler(cf, rc);
    }
    ...
}

static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
    for(i = 0; cf->cycle->modules[i]; i++){
        cmd = cf->cycle->modules[i]->commands;
        if(cmd == NULL){
            continue;
        }

        for( ; cmd->name.len; cmd++ ){
            if( name->len != cmd->name.len){
                continue;
            }
            if(ngx_strcmp(name->data, cmd->name.data) != 0){
                continue;
            }
            found = 1;
            ...
            rc = cmd->set(cf, cmd, conf);
        }
    }
}

ngx_conf_parse函数支持三种不同的解析环境:

  • parse_file:解析配置文件
  • parse_block: 解析块配置项
  • parse_param:解析命令行配置。(注:命令行配置不支持块配置项)

从前面的解析流程可以看出,首次调用ngx_conf_parse进行配置解析时,类型为parse_file,当递归调用时,ngx_conf_parse的filename参数均设置为空,即正在进行块配置项的解析,例如:

static char * ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ...
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;

    rv = ngx_conf_parse(cf, NULL);
    ...
}


在ngx_conf_handler的校验过程中会针对模块类型、配置项类型、配置项值个数等进行校验。

  • 模块类型:校验配置项所属的模块是否为当前处理配置项解析的模块。
if( cf->cycle->modules[i]->type != NGX_CONF_MODULE && 
    cf->cycle->modules[i]->type != cf->module_type ){
    continue;
}

    cf->module_type 初始值设置为 NGX_CORE_MODULE(在ngx_init_cycle函数中初始化),解析到配置项 http { 的时候,找到ngx_http_module模块,并调用ngx_http_block处理该配置项。从ngx_http_module模块的定义可以看到其类型确实为NGX_CORE_MODULE。

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

ngx_module_t ngx_http_module = {
    NGX_MODULE_V1,
    &ngx_http_module_ctx,
    ngx_http_commands,
    NGX_CORE_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_P1_PADDING
};

在ngx_http_block中将cf->module_type设置为NGX_HTTP_MODULE,解析http块配置项中的其他配置项时,找到对应的http模块并进行后续处理,而所有http模块的类型均定义为NGX_HTTP_MODULE。 这样,可有效防止配置项被其他模块所处理。

  • 配置项类型:与模块类型类似
if(!(cmd->type & cf->cmd_type)){
    continue;
}

    cmd->type初始值为NGX_MAIN_CONF,前面提到的ngx_http_module模块的配置项数组中http配置项的类型包含了NGX_MAIN_CONF。

  • 配置项值的个数

    前面提到每个配置项名后可以有多个配置项值,当前配置项值的最大个数定义为8。代码中有相关的宏对应。配置项值的个数必须正确才能进行实际的配置项解析。

#define  NGX_CONF_MAX_ARGS  8
#define  NGX_CONF_NOARGS    0x00000001  //无配置项值
#define  NGX_CONF_TAKE1     0x00000002  //接受1个配置项值
#define  NGX_CONF_TAKE2     0x00000004  //接受2个配置项值
#define  NGX_CONF_TAKE3     0x00000008  //接受3个配置项值
#define  NGX_CONF_TAKE4     0x00000010  //接受4个配置项值
#define  NGX_CONF_TAKE5     0x00000020  //接受5个配置项值
#define  NGX_CONF_TAKE6     0x00000040  //接受6个配置项值
#define  NGX_CONF_TAKE7     0x00000080  //接受7个配置项值
#define  NGX_CONF_BLOCK     0x00000100  //接受块配置项
#define  NGX_CONF_FLAG      0x00000200  //接受配置项值为on或者off
#define  NGX_CONF_ANY       0x00000400  //接受任意的配置项值
#define  NGX_CONF_1MORE     0x00000800  //至少1个配置项值
#define  NGX_CONF_2MORE     0x00001000  //至少2个配置项值
#define  NGX_CONF_MULTI     0x00000000  //接受多个配置项值 个数不定

//接受1个或2个配置项值
#define NGX_CONF_TAKE12     (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
//接受1个或3个配置项值
#define NGX_CONF_TAKE13     (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
//接受2个或3个配置项值
#define NGX_CONF_TAKE23     (NGX_CONF_TAKE2|NGX_CONF_TAKE2)
//接受1个或2个或3个配置项值
#define NGX_CONF_TAKE123    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
//接受1个或2个或4个配置项值
#define NGX_CONF_TAKE1234   (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3|NGX_CONF_TAKE4)

参考:

《深入理解nginx》

《nginx开发从入门到精通》

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!