nginx http模塊數(shù)據(jù)存儲結(jié)構(gòu)小結(jié)
從本節(jié)開始,我們將進入http模塊實現(xiàn)原理的講解,關(guān)于http模塊,有一個非常重要的點就是其是如何存儲http塊、server塊和location塊的數(shù)據(jù)的,而且nginx有的配置項是可以在多個配置塊中使用的,當(dāng)http塊、server塊和location塊中兩個或者兩個以上的配置塊都配置了該配置項的時候,就會有一個問題是,nginx是如何處理這些配置項的。本文主要講解http塊中的各個模塊數(shù)據(jù)的存儲方式,這將是理解nginx的http模塊的工作方式的重要基石。
1. 核心模塊的存儲方式
在nginx運行過程中,有一個全局配置結(jié)構(gòu)體 ngx_cycle_t
,其有一個屬性 conf_ctx
,這個屬性是存儲nginx所有模塊配置的一個數(shù)組,這個數(shù)組的長度與nginx模塊的個數(shù)相同。不過需要注意的是, conf_ctx
數(shù)組的第一維只會存儲核心模塊的配置,而其他模塊對應(yīng)的位置處的數(shù)組元素其實是為NULL。在 conf_ctx
中,各個核心模塊配置結(jié)構(gòu)體的存儲位置與該模塊在所有模塊(包括非核心模塊)中的相對位置是一致的,如下圖所示為nginx存儲核心模塊的一個結(jié)構(gòu)示意圖:
這里標注的 events
和 http
只是為了展示方便而添加的,本質(zhì)上這個數(shù)組的元素的類型是 void*
的指針,至于該指針指向的具體結(jié)構(gòu)體的類型,則是根據(jù)各個核心模塊自身的定義來的。
在http模塊下,其指向了一個 ngx_http_conf_ctx_t
類型的結(jié)構(gòu)體,這個結(jié)構(gòu)體的作用就是用來存儲http配置塊中各個配置項的數(shù)據(jù)的。如下是這個結(jié)構(gòu)體的定義:
typedef struct { // 存儲MAIN級別配置 void **main_conf; // 存儲SRV級別配置 void **srv_conf; // 存儲LOC級別配置 void **loc_conf; } ngx_http_conf_ctx_t;
我們知道,在nginx.conf配置文件中,在http塊下還配置有server塊,而server塊下也是可以有l(wèi)ocation塊,更有甚者,在location塊下可以有子location塊,如此往復(fù),而這里的 ngx_http_conf_ctx_t
結(jié)構(gòu)體的作用就是存儲所有的這些配置所對應(yīng)的結(jié)構(gòu)體數(shù)據(jù)。首先,我們需要明確的一點是,在nginx.conf配置文件中,配置項都是由一個個模塊定義的,一個模塊可以定義多個配置項,對于這些配置項的解析工作都是由這個模塊所定義的方法進行的。但是,一般的,一個模塊一般都只會定義一個結(jié)構(gòu)體,這個結(jié)構(gòu)體中的各個屬性則對應(yīng)于該模塊所定義的各個配置項的數(shù)據(jù),也就是說,通過各個模塊所定義的方法,其會將其所定義的配置項對應(yīng)的配置轉(zhuǎn)換為該模塊所定義的結(jié)構(gòu)體。這里所說的結(jié)構(gòu)體就對應(yīng)于上面的 main_conf
、 srv_conf
和 loc_conf
中的配置。從上面的定義就可以看出,這三個屬性的類型都是指針類型的數(shù)組,而數(shù)組的長度就對應(yīng)于模塊的個數(shù),準確來講,是對應(yīng)于http模塊的各個。在解析各個http模塊的配置之前,nginx會對各個http模塊在當(dāng)前類型的模塊(http模塊)中進行相對位置進行標記,每個http模塊的相對位置就對應(yīng)于上面的三個屬性的數(shù)組下標。前面已經(jīng)講到,每個http模塊都只會有一個配置結(jié)構(gòu)體存儲該模塊所定義的所有配置數(shù)據(jù),而這些配置結(jié)構(gòu)體就是存儲在上面的三個數(shù)組中的。這樣,我們就能夠理解了,其實上面的結(jié)構(gòu)體的三個屬性,每一個屬性的數(shù)組都對應(yīng)了一個http模塊的配置結(jié)構(gòu)體。
既然這里每個模塊都有一個結(jié)構(gòu)體存儲在數(shù)組的對應(yīng)索引位置,那這里為什么需要三個數(shù)組呢?比如說,對于 ngx_http_core_module
,其相對位置在http模塊是第一個,也就是說 main_conf[0]
、 srv_conf[0]
和 loc_conf[0]
存儲的都是 ngx_http_core_module
的配置結(jié)構(gòu)體,為什么需要三個結(jié)構(gòu)體。這里我們需要說明的是,對于每個http模塊,其會根據(jù)需要將配置項按照可使用范圍劃分為三類:僅用于http塊,可以用于http塊和server塊,以及可以用于http塊、server塊和location塊。每一類配置項都使用的是一個不同的結(jié)構(gòu)體,比如 ngx_http_core_module
就定義了 ngx_http_core_main_conf_t
用于存儲僅用于http塊的配置項,定義了 ngx_http_core_srv_conf_t
用于存儲用于http塊和server塊的配置項,定義了 ngx_http_core_loc_conf_t
用于存儲用于http塊、server塊和location塊的配置項。對應(yīng)于上面的數(shù)組就是, main_conf[0]
的結(jié)構(gòu)體類型為 ngx_http_core_main_conf_t
, srv_conf[0]
的結(jié)構(gòu)體類型為 ngx_http_core_srv_conf_t
, loc_conf[0]
對應(yīng)的結(jié)構(gòu)體類型為 ngx_http_core_loc_conf_t
。說到這里,我們就必須要厘清一個問題了,比如,對于某個配置項,其配置在了http塊中,但是其類型是可以用于http塊、server塊和location塊的,那么其就會被存儲在 loc_conf[0]
中,也就是說,上面的一整個結(jié)構(gòu)體,從目前來看,存儲的都是在http塊中解析出來的各個配置項的數(shù)據(jù)。那么nginx是如何標記一個配置項是這三種類型中的哪一種呢?這主要是通過 ngx_command_t
結(jié)構(gòu)體來定義的,如下所示為三個典型的配置:
{ ngx_string("variables_hash_max_size"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof(ngx_http_core_main_conf_t, variables_hash_max_size), NULL }, { ngx_string("listen"), NGX_HTTP_SRV_CONF | NGX_CONF_1MORE, ngx_http_core_listen, NGX_HTTP_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("root"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_CONF_TAKE1, ngx_http_core_root, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL },
這里我們以 variables_hash_max_size
、 listen
和 root
三個指令為例,這三個指令都是 ngx_http_core_module
模塊定義的配置項,但是它們存儲的位置則是完全不同的。我們需要注意的就是每個指令的第四個屬性的定義: NGX_HTTP_MAIN_CONF_OFFSET
、 NGX_HTTP_SRV_CONF_OFFSET
和 NGX_HTTP_LOC_CONF_OFFSET
。這三個類型的定義有兩重含義,一個是表示這個配置項是僅用于http塊,還是可以用于http塊和server塊,再或者是可以用于http塊、server塊和location塊;另一重含義是定義了這個配置項在上面講的 ngx_http_conf_ctx_t
中的偏移量,所謂的偏移量指的就是,在知道 ngx_http_conf_ctx_t
結(jié)構(gòu)體對象的指針地址時,通過這里的偏移量就可以計算出當(dāng)前配置項所存儲的數(shù)組。這里我們就需要展示一段代碼,即在 ngx_conf_parse()
方法中,其主要是用于解析nginx.conf配置文件的,在解析了某個配置項之后,就會在所有的模塊中,找到該配置項的定義,如果找到了配置項,就會嘗試獲取存儲該配置項所對應(yīng)的結(jié)構(gòu)體,并且會調(diào)用該配置項指定的方法進行配置項數(shù)據(jù)的解析。這里嘗試獲取該配置項所對應(yīng)的結(jié)構(gòu)體時,就需要用上上面的偏移量。如下是獲取該配置項的方法:
// 查找配置對象,NGX_DIRECT_CONF常量單純用來指定配置存儲區(qū)的尋址方法,只用于core模塊 if (cmd->type & NGX_DIRECT_CONF) { conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index]; // NGX_MAIN_CONF常量有兩重含義,其一是指定指令的使用上下文是main(其實還是指core模塊), // 其二是指定配置存儲區(qū)的尋址方法。 } else if (cmd->type & NGX_MAIN_CONF) { conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]); // 除開core模塊,其他類型的模塊都會使用第三種配置尋址方式,也就是根據(jù)cmd->conf的值 // 從cf->ctx中取出對應(yīng)的配置。舉http模塊為例,cf->conf的可選值是NGX_HTTP_MAIN_CONF_OFFSET、 // NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET, // 分別對應(yīng)“http{}”、“server{}”、“l(fā)ocation{}”這三個http配置級別。 // 這個if判斷的作用主要是,cf->ctx的類型是ngx_http_conf_ctx_t,而cmd->conf主要的值可選 // NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET、NGX_HTTP_LOC_CONF_OFFSET, // 可以看到ngx_http_conf_ctx_t的屬性有main_conf、srv_conf和loc_conf, // 其實這里就是在計算當(dāng)前的配置對象是存儲在這三個數(shù)組中的哪一個數(shù)組中,以default_type指令為例, // 其ngx_command_t的配置為: // {ngx_string("default_type"), // NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, // ngx_conf_set_str_slot, // NGX_HTTP_LOC_CONF_OFFSET, // offsetof(ngx_http_core_loc_conf_t, default_type), // NULL}, // 可以看到,其conf屬性的值為NGX_HTTP_LOC_CONF_OFFSET,則說明其是存儲在loc_conf數(shù)組中的, // 而該數(shù)組中的元素類型為ngx_http_core_loc_conf_t,因而可以看到,后面ngx_command_t // 中offset屬性的值就指定為了offsetof(ngx_http_core_loc_conf_t, default_type), // 這就是在計算default_type屬性在ngx_http_core_loc_conf_t結(jié)構(gòu)體中的位置。 // 通過下面的if判斷第一步confp = *(void **) ((char *) cf->ctx + cmd->conf);,就可以 // 計算出當(dāng)前所使用的結(jié)構(gòu)體是在main_conf、srv_conf // 和loc_conf的哪一個數(shù)組中,而通過第二步conf = confp[cf->cycle->modules[i]->ctx_index]; // 的計算,就可以計算出該結(jié)構(gòu)體在數(shù)組中的具體位置,并且獲取該結(jié)構(gòu)體數(shù)據(jù)。 // 需要注意的是,這種計算方式只適用于http模塊的配置項獲取,因為只有http模塊的配置結(jié)構(gòu)體是 // ngx_http_conf_ctx_t類型的 } else if (cf->ctx) { confp = *(void **) ((char *) cf->ctx + cmd->conf); if (confp) { conf = confp[cf->cycle->modules[i]->ctx_index]; } }
這里我們需要重點關(guān)注最后一個 else if
分支,這里就表明了http模塊是如何根據(jù)配置項的定義來計算該配置項所對應(yīng)的結(jié)構(gòu)體的存儲位置的。下面的圖就展示了包含有http塊配置的整體結(jié)構(gòu):
2. server塊的存儲方式
上面我們講到,使用 ngx_http_conf_ctx_t
結(jié)構(gòu)體就可以存儲所有的http塊中的配置項,那么server塊中的配置項是如何存儲的呢?其主要存儲在 ngx_http_core_module
模塊的 main_conf
中,也即上面的 main_conf[0]
所對應(yīng)的 ngx_http_core_main_conf_t
結(jié)構(gòu)體中,該結(jié)構(gòu)體有一個屬性 servers
,這個屬性的類型為 ngx_array_t
,也即一個數(shù)組。也就是說,在每個http配置塊下,每個server配置塊都對應(yīng)于 servers
數(shù)組的一個元素,而數(shù)組的元素類型與http塊的一致,還是 ngx_http_conf_ctx_t
。不過區(qū)別在于,由于當(dāng)前的配置項一定是可用于server塊或者location塊中的,而不是僅僅只能用于http塊中的,因而配置項的類型一定是上面講到的 NGX_HTTP_SRV_CONF_OFFSET
和 NGX_HTTP_LOC_CONF_OFFSET
之一,而不可能是 NGX_HTTP_MAIN_CONF_OFFSET
。因而這里雖然每個server配置塊對應(yīng)的配置結(jié)構(gòu)體還是 ngx_http_conf_ctx_t
,但是其 main_conf
數(shù)組是不會有對應(yīng)的配置項的,而只能從http塊中繼承配置項。既然是繼承,nginx的處理方式是直接將該數(shù)組的指針指向http塊對應(yīng)的 ngx_http_conf_ctx_t
的 main_conf
數(shù)組。如下所示為兩個server塊配置的示意圖:
這個圖稍微看起來有點復(fù)雜,但實際上并不復(fù)雜,按照配置塊劃分,上面的 ngx_http_conf_ctx_t
中存儲的就是http塊的配置,而下面的兩個 ngx_http_conf_ctx_t
存儲的就是兩個server塊中的配置,中間的引用過程是通過http塊的 ngx_http_core_module
模塊對應(yīng)的 ngx_http_core_main_conf_t.servers
進行的。需要注意的一點是,上面的server塊的配置中, main_conf
指針都是指向的http塊的對應(yīng) ngx_http_conf_ctx_t
的 main_conf
屬性。
3. location塊的存儲方式
對于location塊的存儲,其存儲結(jié)構(gòu)也還是 ngx_http_conf_ctx_t
,并且由于當(dāng)前配置項在location塊中的,因而其類型一定不會是 NGX_HTTP_MAIN_CONF_OFFSET
和 NGX_HTTP_SRV_CONF_OFFSET
,也就是說,解析location配置項得到的數(shù)據(jù)一定是存儲在 loc_conf
數(shù)組中的。因而,與server塊一樣,location塊對應(yīng)的 ngx_http_conf_ctx_t
結(jié)構(gòu)體中的 main_conf
和 srv_conf
指向的則是當(dāng)前l(fā)ocation所在的http塊的 main_conf
和所在的server塊的 srv_conf
數(shù)組。
另外,一個server塊下會有多個location塊,在存儲結(jié)構(gòu)上,這些location塊是以隊列的方式進行組織的,與server塊類似,這個隊列則是存儲在其所在的server塊對應(yīng)的 ngx_http_conf_ctx_t
的 loc_conf[0]
中的。這里的 loc_conf[0]
的結(jié)構(gòu)體類型為 ngx_http_core_loc_conf_s
,其有一個 ngx_queue_t
類型的屬性 locations
就是該location隊列。最后需要注意的是,這里的 locations
屬性表征的不僅僅只是server塊下的多個location塊,因為在location配置塊下還可以繼續(xù)配置多個location塊,如此不斷遞歸下去。這些子location塊的類型其實還是 ngx_http_core_loc_conf_s
,因而也是可以通過 locations
屬性進行表征的。如下是加入location配置塊的結(jié)構(gòu)體示意圖:
圖中展示了兩個location并列組織的情形,其 main_conf
和 srv_conf
分別指向了http塊的 main_conf
和當(dāng)前l(fā)ocation塊所在的server塊的 srv_conf
,并且兩個location塊對應(yīng)的結(jié)構(gòu)體是以隊列的方式組織在 ngx_http_core_loc_conf_t
中的。
4. 小結(jié)
本文從 ngx_cycle_t
結(jié)構(gòu)體開始,介紹了http塊的配置項是如何存儲在 ngx_cycle_t
中的,并且依次介紹了http塊、server塊和location塊的存儲方式,以及相互之間的組織方式。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持本站。
版權(quán)聲明:本站文章來源標注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請保持原文完整并注明來源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來,僅供學(xué)習(xí)參考,不代表本站立場,如有內(nèi)容涉嫌侵權(quán),請聯(lián)系alex-e#qq.com處理。