nginx 配置文件解析

首先来看一下,配置文件解析时的数据结构,这里主要是ngx_conf_t,这个结构保存了解析配置文件所需要的一些域,是一个非常重要的数据结构.

struct  ngx_conf_s{
       char                          *name;//当前解析的命令名
       ngx_array_t              *args;  //当前命令的所有参数
       
       ngx_cycle_t               *cycle;
       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;
       char                       *handler_conf          
}

我们来看配置文件的解析人口,入口在ngx_init_cycle中,就是设置ngx_conf_t然后传递给ngx_conf_parse解析.

//创建conf_ctx
cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
    if (cycle->conf_ctx == NULL) {
        ngx_destroy_pool(pool);
        return NULL;
    }
 for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        module = ngx_modules[i]->ctx;

        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
           //这里看到的conf_ctx里面就是放对应模块的main conf
            cycle->conf_ctx[ngx_modules[i]->index] = rv;
        }
    }
    conf.ctx = cycle->conf_ctx;  //初始化conf
    conf.cycle = cycle;
    conf.pool = pool;
    conf.log = log;
    conf.module_type = NGX_CORE_MODULE;//注意,一开始命令的类型就是MAIN,并且模块类型是core。
    conf.cmd_type = NGX_MAIN_CONF;

    if (ngx_conf_param(&conf) != NGX_CONF_OK) {
        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;
    }

    然后我们来看一下 ngx_conf_parse这个函数,这个函数的第二个参数是解析文件名(nginx.conf),不过这里要注意一点,那就是第二个参数是可以为空的,

 如果为空,则说明将要解析的是block或者param.   

har *
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) {
            /* open configuration 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 (cf->handler) {//如果有handler,则调用handler
              rv = (*cf->handler)(cf, NULL, cf->handler_conf);
     }
     ...........
     rc = ngx_conf_handler(cf, rc);//没有handler则调用默认解析函数
}

在看ngx_conf_handler之前,我们先看一下nginx的配置文件,以及为什么调用cf->handler。

worker_processes 1;

error_log logs/error.log;

pid logs/nginx.pid;

events {
worker_connections 1024;
}

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

sendfile on;

keepalive_timeout 65;

#gzip on;

server {
listen 80;
server_name localhost;

access_log logs/host.access.log main;

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

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

}

可以看到nginx配置文件是分级的,然后event,http都是一个core模块,然后core模块中包含了很多2级模块(epoll,kqueue).也就是1级模块中必须包含一个上下文用来保存2级模块的配置信息。而在http模块中又有一些特殊,就是http模块中每个指令都具有3个作用域,那就是main/server/loc,所以在http上下文中,必须保存3个上下文.

然后看一下nginx的命令有那些类型:

#define NGX_DIRECT_CONF      0x00010000
 
#define NGX_MAIN_CONF        0x01000000
#define NGX_ANY_CONF         0x0F000000
 
#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
 
#define NGX_MAIL_MAIN_CONF      0x02000000
#define NGX_MAIL_SRV_CONF       0x04000000

nginx中的参数类型有这么多种,其中最有必要的是(NGX_DIRECT_CONF,NGX_MAIN_CONF),一般来说这2个命令类型是同时使用的。DIRECT_CONF就是直接存取CONF,也就是说进入命令解析函数的同时,conf已经被创建好了,直需直接使用即可.而MAIN_CONF就是最顶层的CONF。如HTTP/EVENT/pid等,可以看到都是core模块。而NGX_HTTP_xxx就是http的子模块.

理解了nginx的配置的基本结构,我们来看ngx_conf_handler函数:

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
      .........
      for (i = 0; ngx_modules[i]; i++) {
        cmd = ngx_modules[i]->commands; //初始话 每个模块中的ngx_command_s
         ........
           conf = NULL;
            ///最核心的地方 
            if (cmd->type & NGX_DIRECT_CONF) {
                conf = ((void **) cf->ctx)[ngx_modules[i]->index];//我们还记得最开始ctx是包含了所有core模块的conf(create_conf回调),因此这里取出对应的模块conf.
            } else if (cmd->type & NGX_MAIN_CONF) {
//如果不是DIRECT_CONF并且是MAIN,则说明我们需要在配置中创建自己模块的上下文(也就是需要进入二级模块) conf
= &(((void **) cf->ctx)[ngx_modules[i]->index]); } else if (cf->ctx) {//否则进入二级模块处理 confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[ngx_modules[i]->ctx_index]; } } rv = cmd->set(cf, cmd, conf); //ngx_commands_s 中set函数回调 .... }

我们以http模块举例:

//可以看到没有direct_conf,是因为http有2级模块
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
};
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;
 
    /* the main http context */
 
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }
//最核心的地方,可以看到修改了传递进来的conf
    *(ngx_http_conf_ctx_t **) conf = ctx;
 
    /* count the number of the http modules and set up their indices */
 
    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++;
    }
 
 
    /* the http main_conf context, it is the same in the all http contexts */
//创建HTTP对应的conf,因为每个级别(main/ser/loc)都会包含模块的conf.
    ctx->main_conf = ngx_pcalloc(cf->pool,
                                 sizeof(void *) * ngx_http_max_module);
    if (ctx->main_conf == NULL) {
        return NGX_CONF_ERROR;
    }
 
 
    /*
     * the http null srv_conf context, it is used to merge
     * the server{}s' srv_conf's
     */
 
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }
 
 
    /*
     * the http null loc_conf context, it is used to merge
     * the server{}s' loc_conf's
     */
 
    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
     */
....................................
//保存当前使用的cf,因为我们只是在解析HTTP时需要改变当前的cf,
    pcf = *cf;
//保存当前模块的上下文
    cf->ctx = ctx;
..........................................
 
    /* 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
     */
.........................................
    /*
     * http{}'s cf->ctx was needed while the configuration merging
     * and in postconfiguration process
     */
//回复cf
    *cf = pcf;
}

这里有个非常关键的地方,那就是在每个级别都会保存对应的ctx(main/ser/loc),怎么说呢,就是在解析HTTP main中创建了3个ctx(main/srv/loc),而在HTTP srv block中将会创建2个ctx(main/srv/loc),或许会问重复了怎么办?重复了,那就需要merge了。比如一个命令(srv_offset)在HTTP main中有一个,那么Nginx将会把它放入到HTTP main的ctx的srv ctx中,然后server block也有一个,那么Nginx会继续把它放到Server ctx的 srv_conf中,最后merge他们。

 { ngx_string("server"),
      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS,
      ngx_http_core_server,
      0,
      0,
      NULL },
 
static char *
ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char                        *rv;
    void                        *mconf;
    ngx_uint_t                   i;
    ngx_conf_t                   pcf;
    ngx_http_module_t           *module;
    struct sockaddr_in          *sin;
    ngx_http_conf_ctx_t         *ctx, *http_ctx;
    ngx_http_listen_opt_t        lsopt;
    ngx_http_core_srv_conf_t    *cscf, **cscfp;
    ngx_http_core_main_conf_t   *cmcf;
 
    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }
 
    http_ctx = cf->ctx;
//main conf不变
    ctx->main_conf = http_ctx->main_conf;
 
    /* the server{}'s srv_conf */
//创建新的srv和loc conf.
    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }
 
    /* the server{}'s loc_conf */
 
    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }
............................
 
    /* the server configuration context */
 
    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];
    cscf->ctx = ctx;
 
 
    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];
//保存所有的servers,可以看到是保存在main中的。这样子最后在HTTP main中就可以取到这个srv conf.
    cscfp = ngx_array_push(&cmcf->servers);
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }
 
    *cscfp = cscf;
 
 
    /* parse inside server{} */
//解析,可以看到设置type为srv_conf.
    pcf = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_HTTP_SRV_CONF;
 
    rv = ngx_conf_parse(cf, NULL);
//恢复cf.
    *cf = pcf;
}

然后来看一些简单的命令是如何使用和配置的。在看之前先来看几个核心的结构:

typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t;
 
//下面这些就是放到ngx_command_t的conf域,可以看到就是对应conf的偏移.
#define NGX_HTTP_MAIN_CONF_OFFSET  offsetof(ngx_http_conf_ctx_t, main_conf)
#define NGX_HTTP_SRV_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, srv_conf)
#define NGX_HTTP_LOC_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, loc_conf)
 
//下面就是如何来取模块的配置
#define ngx_http_get_module_main_conf(r, module)                             \
    (r)->main_conf[module.ctx_index]
#define ngx_http_get_module_srv_conf(r, module)  (r)->srv_conf[module.ctx_index]
#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]
 
 
#define ngx_http_conf_get_module_main_conf(cf, module)                        \
    ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
#define ngx_http_conf_get_module_srv_conf(cf, module)                         \
    ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
#define ngx_http_conf_get_module_loc_conf(cf, module)                         \
    ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]
 
#define ngx_http_cycle_get_module_main_conf(cycle, module)                    \
    (cycle->conf_ctx[ngx_http_module.index] ?                                 \
        ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index])      \
            ->main_conf[module.ctx_index]:                                    \
        NULL)
 
#define ngx_get_conf(conf_ctx, module)  conf_ctx[module.index]

 

 

 

 

posted @ 2012-11-27 14:57  风去无痕  阅读(2775)  评论(0编辑  收藏  举报