nginx源碼分析——配置

1. 配置介紹

nginx的配置由一個主配置文件和其餘一些輔助的配置文件構成。這些配置文件均是純文本文件,這些配置文件所有位於nginx安裝目錄下的conf目錄中。html

主配置文件nginx.conf中的內容大概是這樣子的:nginx

#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分隔。簡單配置項的結尾使用分號結束,其基本的語法格式以下:bash

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

塊配置項由一個配置項名和一對大括號組成,其結尾不須要再添加分號。塊配置項使用大括號把一系列所屬的配置項所有包含起來,表示大括號內的配置項同時生效;塊配置項也能夠有配置項值,這取決於解析這個配置項的模塊;塊配置項能夠嵌套,內層塊直接繼承外層塊,同一個配置項能夠同時出如今內外層塊配置項中,其優先級也取決於解析該配置項的模塊。app

2. 配置解析流程

nginx配置解析的流程基本上是一個遞歸的過程,以下圖所示:函數

在配置解析的入口函數 ngx_conf_parse 中會循環讀取配置文件中的內容,每讀取一行後就解析該配置項。解析的過程當中可能會再次調用該函數完成後續配置項的解析,例如events塊配置項,http塊配置項的解析。spa

對應的源碼:命令行

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函數支持三種不一樣的解析環境:code

  • 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開發從入門到精通》

相關文章
相關標籤/搜索