1. 程式人生 > >http模組初始化過程

http模組初始化過程

要理解一個核心部分,模組的初始化概括起來就是申請儲存下一級模組配置結構體的空間,並且呼叫相應的回撥生成模組的配置結構體,然後再開始通過ngx_conf_t指定的內容去解析配置檔案,進行配置指令的儲存
1.ngx_http_module_t
所有http模組都是ngx_http_module_t型別,所有的屬性都是回撥函式,在http模組初始化過程的不同階段呼叫。當一個指令既允許出現在main塊(在http{}塊內,但是在server{}塊外的區域)、server塊(在server{}塊內,但是在location{}塊外的區域)、location塊內時,就需要對這些指令進行繼承和覆蓋的處理,由merge_src_conf和merge_loc_conf完成。
[cpp] view plain copy print?
typedef struct {
/**
* 在解析配置檔案中http{}配置塊前呼叫
*/
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);

/** 
 * 在解析配置檔案中http{}配置塊後呼叫 
 */  
ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);  

/** 
 * 建立http模組的main config 
 */  
void       *(*create_main_conf)(ngx_conf_t *cf);  

/** 
 * 初始化http模組的main config 
 */  
char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);  

/** 
 * 建立http模組的server config 
 */  
void       *(*create_srv_conf)(ngx_conf_t *cf);  

/** 
 * 合併http模組的server config,用於實現server config到main config的指令的繼承、覆蓋 
 */  
char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);  

/** 
 * 建立http模組的location config 
 */  
void       *(*create_loc_conf)(ngx_conf_t *cf);  

/** 
 * 合併http模組的location config,用於實現location config到server config的指令的繼承、覆蓋 
 */  
char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);  

} ngx_http_module_t;
2.ngx_http_conf_ctx_t
此結構用於儲存所有http模組的main、server和location的config結構。可以看到ngx_http_conf_ctx_t的三個屬性就是三個陣列,陣列大小由ngx_http_max_module指定的,這個變數在指令http塊的回撥函式時初始化。即ngx_http_block(),而每一個模組的ctx_index屬性就指示了模組的配置結構在三個數組裡面的下標
nginx提供了倆類巨集用來獲取對應得配置塊
1》#define ngx_http_conf_get_module_main_conf(r,module)
r是請求,module是具體的http模組
2》#define ngx_conf_get_module_main_conf(cf,moduole)
cf是ngx_conf_t型別,module是http模組
3.http模組的啟動過程:
基本上是核心模組負責解析對應得模組,比如ngx_http_module負責解析http的其他模組
1》ngx_http_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  

};

static ngx_core_module_t ngx_http_module_ctx = {
ngx_string(“http”),
NULL,
NULL
};

ngx_module_t ngx_http_module = {
NGX_MODULE_V1,
&ngx_http_module_ctx, /* module context */
ngx_http_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
可以看到它的type型別有ngx_conf_block,那麼表示它是一個塊指令,所以在解析所有核心模組的時候,如果遇到了http,那麼就會呼叫ngx_http_block,來解析http模組的內容
2》ngx_http_block
先熟悉它的內部變數
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賦值為ctx,即ngx_http_conf_ctx_t型別。
* 這個conf是ngx_cycle->conf_ctx陣列中的元素,而這個元素就是
* ngx_http_module模組對應的config資訊。所以這一步就完成了
* ngx_http_module模組config資訊的初始化。
*/
(ngx_http_conf_ctx_t *) conf = ctx;
是為了改變那個一級指標的內容,也就是剛好初始化了那個陣列中的內容
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++;  

}
//更新每個模組的ctx_index屬性
/* 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;
}
//接下來是根據ngx_http_max_module的大小給ctx的main,srv,loc分配空間
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[m]->ctx;  
mi = ngx_modules[m]->ctx_index;  

if (module->create_main_conf) {  
    ctx->main_conf[mi] = module->create_main_conf(cf);  
    if (ctx->main_conf[mi] == NULL) {  
        return NGX_CONF_ERROR;  
    }  
}  

if (module->create_srv_conf) {  
    ctx->srv_conf[mi] = module->create_srv_conf(cf);  
    if (ctx->srv_conf[mi] == NULL) {  
        return NGX_CONF_ERROR;  
    }  
}  

if (module->create_loc_conf) {  
    ctx->loc_conf[mi] = module->create_loc_conf(cf);  
    if (ctx->loc_conf[mi] == NULL) {  
        return NGX_CONF_ERROR;  
    }  
} 
每個不同的模組通過create_main_conf等三個函式生成不同的結構體,比如ngx_http_core_module對應生成的是ngx_http_core_main_conf_t結構體,通過create_srv_conf生成的是ngx_http_core_srv_conf_t結構體,同時存入對應得main_conf陣列,srv_conf陣列中去。

/* 先儲存cf的副本,待所有http module的指令解析完再恢復 */
pcf = *cf; //cf對應得是核心模組的結構,這會應該換成對應http模組的結構,因為要解析http模組了
/* 把解析http module的指令的上下文設定為ngx_http_conf_ctx_t */
cf->ctx = ctx;

/* 呼叫http module的preconfiguration回撥函式 */
for (m = 0; ngx_modules[m]; m++) {
if (ngx_modules[m]->type != NGX_HTTP_MODULE) {
continue;
}

module = ngx_modules[m]->ctx;  

if (module->preconfiguration) {  
    if (module->preconfiguration(cf) != NGX_OK) {  
        return NGX_CONF_ERROR;  
    }  
}  

}

cf->module_type = NGX_HTTP_MODULE; // 只解析NGX_HTTP_MODULE模組的指令,即http module的指令
cf->cmd_type = NGX_HTTP_MAIN_CONF; // 只解析NGX_HTTP_MAIN_CONF型別的指令
/* 只有符合module_type和cmd_type的指令才會被解析 */
rv = ngx_conf_parse(cf, NULL)
有關該配置檔案的具體解析過程,詳細見配置檔案的解析//
在ngx_conf_parse函式呼叫完成以後,所有的配置指令都已經解析完畢

接下來呼叫所有http module的init_main_conf回撥函式初始化main config,ngx_http_merge_servers函式中會呼叫模組的merge_srv_conf和merge_loc_conf回撥函式進行合併

for (s = 0; s < cmcf->servers.nelts; s++) {

clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];  

if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {5  
    return NGX_CONF_ERROR;  
}  

if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {  
    return NGX_CONF_ERROR;  
}  

}
//將location塊組織成樹狀的結構

if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {
return NGX_CONF_ERROR;
}
//nginx把請求的處理過程分為了11個階段,除了某幾個以外其他的都可以註冊phase handler,在ngx_http_core_main_conf_t的結構體中的phases中儲存了所以phase handler陣列,這裡完成每個phase的handler初始化(詳細見phase handler初始化)
if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {
//把http請求的header初始化成hash結構,在ngx_http_headers_in陣列中儲存了所有的header,這裡是根據該陣列去初始化
呼叫所有http module的postconfiguration回撥函式。
//呼叫配置檔案解析完的postrconfiguration函式
ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK)
//初始化phase handler,nginx把所有的phase handler存放在ngx_http_core_main_conf_t的phase_engine的handlers陣列中,該結構是ngx_http_phase_handler_t型別的,它裡面的一個next屬性指示這該handlers陣列的下標,那麼處理請求的時候只需要遍歷該陣列就可以
(ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK)
它完成ngx_http_conf_addr_t結構中的hash(server_name為key,ngx-http_core_srv_conf_t為值得hash結構),
最後呼叫ngx_http_init_listening函式,(具體過程詳見監聽socket初始化)