天天看點

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架構配置記憶體布局

繼續閱讀