天天看点

nginx的http模块配置接口以及http框架配置内存布局

http模块是nginx中数量最多,也是配置最复杂的模块。http模块的配置最多可以分为三级,分别是http级别,server级别和location级别。nginx为http模块定义了一组接口,用于模块创建,设置,合并各级别的配置项。在配置读取阶段一共有9个接口,在不同阶段,不同位置被调用。其中8个在ngx_http_module_t中定义,这8个回调是模块级别的,还有1个是指令级别的,由模块的指令定义。ngx_module_t定义如下所示:

typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);//准备读取配置文本前
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);//读取并解析配置结束后

    void       *(*create_main_conf)(ngx_conf_t *cf);//创建main级别上下文
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);//初始化main级别上下文

    void       *(*create_srv_conf)(ngx_conf_t *cf);//创建server级别配置上下文
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//合并server级别配置上下文

    void       *(*create_loc_conf)(ngx_conf_t *cf);//创建location级别配置上下文
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);//合并location级别配置上下文
} ngx_http_module_t;
           

nginx在解析配置文件的过程,会依次调用这些函数,它们的调用顺序如下:

1. create_main_conf

2. create_srv_conf

3. create_loc_conf

4. preconfiguration

5. 读取配置后执行指令的set回调

6. init_main_conf

7. merge_srv_conf

8. merge_loc_conf

9. postconfiguration

每个http模块可以根据自己的需要实现对应的接口,http框架会在合适的位置调用这些接口。

前面介绍的nginx的配置的内存布局一问中说过,cycle的conf_ctx成员保存的是各NGX_CORE_MODULE类型的模块的配置地址。与http密切相关的NGX_CORE_MODULE类型的模块是ngx_http_module,它的配置的实际类型是ngx_http_conf_ctx_t *。ngx_http_module模块只有一个指令,就是"http",如下所示:(关于ngx_http_module和http指令的更详细的分析参考这篇文章)

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块指令是nginx配置文件中进行http相关配置的唯一入口,当nginx遇到http块指令时,就会创建一个ngx_http_conf_ctx_t结构体用来保存http块内的配置,如下代码所示:

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;

    if (*(ngx_http_conf_ctx_t **) conf) {
        return "is duplicate";
    }

    /* the main http context */

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    *(ngx_http_conf_ctx_t **) conf = ctx;


    /* count the number of the http modules and set up their indices */

    ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);


    /* the http main_conf context, it is the same in the all http contexts */

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

另外,http块下的server块和location块的配置也是使用ngx_http_conf_ctx_t作为配置的载体,如下代码所示(ngx_http_core_server是遇到server指令时执行的回调,而ngx_http_core_location是遇到location指令时执行的回调):

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;
    ctx->main_conf = http_ctx->main_conf;

    /* the server{}'s srv_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;
    }
    ...
}
           
static char *
ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
{
    char                      *rv;
    u_char                    *mod;
    size_t                     len;
    ngx_str_t                 *value, *name;
    ngx_uint_t                 i;
    ngx_conf_t                 save;
    ngx_http_module_t         *module;
    ngx_http_conf_ctx_t       *ctx, *pctx;
    ngx_http_core_loc_conf_t  *clcf, *pclcf;

    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    pctx = cf->ctx;
    ctx->main_conf = pctx->main_conf;
    ctx->srv_conf = pctx->srv_conf;

    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);
    if (ctx->loc_conf == NULL) {
        return NGX_CONF_ERROR;
    }
    ...
}
           

也就是说,当碰到server块指令和location块指令的时候,nginx也会创建一个ngx_http_conf_ctx_t结构体用来保存块内的配置。为什么三个级别的配置都使用这个结构体来保存呢,它们之间有什么关系?先看ngx_http_conf_ctx_t的定义。 ngx_http_conf_ctx_t 有三个成员main_conf,srv_conf和loc_conf,如下所示:

typedef struct {
    void        **main_conf;
    void        **srv_conf;
    void        **loc_conf;
} ngx_http_conf_ctx_t;
           

我们先分析main_conf成员。我们先从http块指令分配的ngx_http_conf_ctx_t的main_conf开始。它指向一个数组,数组大小与nginx中http模块(module_type为NGX_HTTP_MODULE的模块)的数量一致,数组元素是各http模块在main级别的配置的地址(由模块的create_main_conf返回),http模块的ctx_index字段就是它们在这个数组中的下标。main_conf数组的第一个元素,也就是ctx_index为0的元素是ngx_http_core_module的main级别的配置地址。ngx_http_core_module是http最核心的模块之一,实际上它和ngx_http_module构成了http的框架,它不仅实现了很多http块内的重要指令,例如listen,server,location等,它所保存的main,srv,location级别的配置也是nginx的http框架运行所依赖的核心数据(为了避免篇幅过长,ngx_http_core_module的分析会在后续单独开篇讲述)。ngx_http_core_module的main级别配置的实际类型是ngx_http_core_main_conf_t,如下所示:

typedef struct {
    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */

    ngx_http_phase_engine_t    phase_engine;

    ngx_hash_t                 headers_in_hash;

    ngx_hash_t                 variables_hash;

    ngx_array_t                variables;         /* ngx_http_variable_t */
    ngx_array_t                prefix_variables;  /* ngx_http_variable_t */
    ngx_uint_t                 ncaptures;

    ngx_uint_t                 server_names_hash_max_size;
    ngx_uint_t                 server_names_hash_bucket_size;

    ngx_uint_t                 variables_hash_max_size;
    ngx_uint_t                 variables_hash_bucket_size;

    ngx_hash_keys_arrays_t    *variables_keys;

    ngx_array_t               *ports;

    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */

    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;
           

可以看到,ngx_http_core_main_conf_t保存的都是全局的配置,比如server信息,http变量,http请求头部,http框架等配置,这里我们只关注servers字段,因为它与http框架配置的内存布局有直接关系。servers是一个动态数组,它的元素是各server块的server 级别配置的地址。每次http框架解析完一个server块时,都会把该server块的配置地址保存在这个数组中。因此,http块内有几个server块,servers的元素就有几个。前面说到,当遇到server块指令和location块指令的时候,nginx也会创建一个ngx_http_conf_ctx_t来保存配置,那么,它们的main_conf成员保存的又是什么内容呢?实际上,它们的main_conf成员与http块指令创建的ngx_http_conf_ctx_t的main_conf成员指向的是同一块内存,即各http模块在main级别的配置数组的首地址。这是因为http main级别的配置只有一份,这份配置是全局通用的,把server和location级别的main_conf指向main级别的main_conf,可以让server和location内的模块也能访问到这些配置,又不浪费内存。

下面再分析srv_conf。依旧先从http块指令创建的ngx_http_conf_ctx_t的srv_conf开始。它和main_conf一样,也指向一个数组,数组大小与nginx中http模块(module_type为NGX_HTTP_MODULE的模块)的数量一致,数组元素是各http模块在server级别的配置的地址(由模块的create_srv_conf返回),http模块的ctx_index字段就是它们在这个数组中的下标。数组的第一个元素是ngx_http_core_module在main级别的server配置的地址,配置的实际类型是ngx_http_core_srv_conf_t,如下所示:

typedef struct {
    /* array of the ngx_http_server_name_t, "server_name" directive */
    ngx_array_t                 server_names;

    /* server ctx */
    ngx_http_conf_ctx_t        *ctx;

    ngx_str_t                   server_name;

    size_t                      connection_pool_size;
    size_t                      request_pool_size;
    size_t                      client_header_buffer_size;

    ngx_bufs_t                  large_client_header_buffers;

    ngx_msec_t                  client_header_timeout;

    ngx_flag_t                  ignore_invalid_headers;
    ngx_flag_t                  merge_slashes;
    ngx_flag_t                  underscores_in_headers;

    unsigned                    listen:1;
#if (NGX_PCRE)
    unsigned                    captures:1;
#endif

    ngx_http_core_loc_conf_t  **named_locations;
} ngx_http_core_srv_conf_t;
           

那么server块和location块指令创建的ngx_http_conf_t的srv_conf保存的又是什么呢?对于server块来说,srv_conf保存的是各http模块在server级别的server配置的地址,上面说到的ngx_http_core_main_conf_t的servers成员保存的就是各server块的srv_conf的第一个元素的地址。main级别和server级别可以同时存在server配置,这样http模块可以自由选择如何合并main级别和server级别的server配置(通过merge_srv_conf回调函数实现)。对于location块来说,srv_conf保存的则是其所属的server级别的server配置的地址,因为在同一个server块内,server相关的配置只有一份,所以server块内的所有location都共享这一份配置。

最后再来分析loc_conf。依旧先从http块指令创建的ngx_http_conf_ctx_t的loc_conf开始。它与main_conf和srv_conf一样,也指向一个数组,数组大小与nginx中http模块(module_type为NGX_HTTP_MODULE的模块)的数量一致,数组元素是各http模块在location级别的配置的地址(由模块的create_loc_conf返回),http模块的ctx_index字段就是它们在这个数组中的下标。数组的第一个元素是ngx_http_core_module的main级别的location配置的地址。配置的实际类型是ngx_http_core_loc_conf_t,如下所示:

struct ngx_http_core_loc_conf_s {
    ngx_str_t     name;          /* location name */

#if (NGX_PCRE)
    ngx_http_regex_t  *regex;
#endif

    unsigned      noname:1;   /* "if () {}" block or limit_except */
    unsigned      lmt_excpt:1;
    unsigned      named:1;

    unsigned      exact_match:1;
    unsigned      noregex:1;

    unsigned      auto_redirect:1;
#if (NGX_HTTP_GZIP)
    unsigned      gzip_disable_msie6:2;
    unsigned      gzip_disable_degradation:2;
#endif

    ngx_http_location_tree_node_t   *static_locations;
#if (NGX_PCRE)
    ngx_http_core_loc_conf_t       **regex_locations;
#endif

    /* pointer to the modules' loc_conf */
    void        **loc_conf;

    uint32_t      limit_except;
    void        **limit_except_loc_conf;

    ngx_http_handler_pt  handler;

    /* location name length for inclusive location with inherited alias */
    size_t        alias;
    ngx_str_t     root;                    /* root, alias */
    ngx_str_t     post_action;

    ngx_array_t  *root_lengths;
    ngx_array_t  *root_values;

    ngx_array_t  *types;
    ngx_hash_t    types_hash;
    ngx_str_t     default_type;

    off_t         client_max_body_size;    /* client_max_body_size */
    off_t         directio;                /* directio */
    off_t         directio_alignment;      /* directio_alignment */

    size_t        client_body_buffer_size; /* client_body_buffer_size */
    size_t        send_lowat;              /* send_lowat */
    size_t        postpone_output;         /* postpone_output */
    size_t        limit_rate;              /* limit_rate */
    size_t        limit_rate_after;        /* limit_rate_after */
    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
    size_t        read_ahead;              /* read_ahead */

    ngx_msec_t    client_body_timeout;     /* client_body_timeout */
    ngx_msec_t    send_timeout;            /* send_timeout */
    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
    ngx_msec_t    lingering_time;          /* lingering_time */
    ngx_msec_t    lingering_timeout;       /* lingering_timeout */
    ngx_msec_t    resolver_timeout;        /* resolver_timeout */

    ngx_resolver_t  *resolver;             /* resolver */

    time_t        keepalive_header;        /* keepalive_timeout */

    ngx_uint_t    keepalive_requests;      /* keepalive_requests */
    ngx_uint_t    keepalive_disable;       /* keepalive_disable */
    ngx_uint_t    satisfy;                 /* satisfy */
    ngx_uint_t    lingering_close;         /* lingering_close */
    ngx_uint_t    if_modified_since;       /* if_modified_since */
    ngx_uint_t    max_ranges;              /* max_ranges */
    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */

    ngx_flag_t    client_body_in_single_buffer;
                                           /* client_body_in_singe_buffer */
    ngx_flag_t    internal;                /* internal */
    ngx_flag_t    sendfile;                /* sendfile */
    ngx_flag_t    aio;                     /* aio */
    ngx_flag_t    aio_write;               /* aio_write */
    ngx_flag_t    tcp_nopush;              /* tcp_nopush */
    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
    ngx_flag_t    absolute_redirect;       /* absolute_redirect */
    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
    ngx_flag_t    port_in_redirect;        /* port_in_redirect */
    ngx_flag_t    msie_padding;            /* msie_padding */
    ngx_flag_t    msie_refresh;            /* msie_refresh */
    ngx_flag_t    log_not_found;           /* log_not_found */
    ngx_flag_t    log_subrequest;          /* log_subrequest */
    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
    ngx_uint_t    server_tokens;           /* server_tokens */
    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
    ngx_flag_t    etag;                    /* etag */

#if (NGX_HTTP_GZIP)
    ngx_flag_t    gzip_vary;               /* gzip_vary */

    ngx_uint_t    gzip_http_version;       /* gzip_http_version */
    ngx_uint_t    gzip_proxied;            /* gzip_proxied */

#if (NGX_PCRE)
    ngx_array_t  *gzip_disable;            /* gzip_disable */
#endif
#endif

#if (NGX_THREADS || NGX_COMPAT)
    ngx_thread_pool_t         *thread_pool;
    ngx_http_complex_value_t  *thread_pool_value;
#endif

#if (NGX_HAVE_OPENAT)
    ngx_uint_t    disable_symlinks;        /* disable_symlinks */
    ngx_http_complex_value_t  *disable_symlinks_from;
#endif

    ngx_array_t  *error_pages;             /* error_page */
    ngx_http_try_file_t    *try_files;     /* try_files */

    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */

    ngx_open_file_cache_t  *open_file_cache;
    time_t        open_file_cache_valid;
    ngx_uint_t    open_file_cache_min_uses;
    ngx_flag_t    open_file_cache_errors;
    ngx_flag_t    open_file_cache_events;

    ngx_log_t    *error_log;

    ngx_uint_t    types_hash_max_size;
    ngx_uint_t    types_hash_bucket_size;

    ngx_queue_t  *locations;

#if 0
    ngx_http_core_loc_conf_t  *prev_location;
#endif
};
           

再看server块指令创建的ngx_http_conf_ctx_t的loc_conf成员,它也指向一个数组,数组大小与http模块数量一致,数组保存的是各http模块在server级别的location配置地址。数组的第一个元素是ngx_http_core_module在该server下的location级别的配置,类型也是ngx_http_core_loc_conf_t,其中的locations字段是一个双向链表,把该server下的所有location配置串联在一起。

最后,location块指令创建的ngx_http_conf_ctx_t的loc_conf成员同样也指向一个数组,数组大小与http模块数量一致,数组保存的是各http模块在location级别的location配置地址。数组的第一个元素是ngx_http_core_module在该location下的location界别的配置,类型也是ngx_http_core_loc_conf_t,其中的loc_conf字段指向该location所属的server级别的loc_conf数组的地址。通过这样方式,各http模块可以自由选择如何合并main,server和location级别的location配置(通过merge_loc_conf回调实现)。

下图综合展示了http框架模块配置的内存布局:

nginx的http模块配置接口以及http框架配置内存布局

继续阅读