nginx 配置解析(11)——merge
以前我們詳細了分析了 nginx 配置解析的各個過程,尤其是各種 conf 結構的建立、初始化、儲存。那麼現在我們要分析下 merge 的過程,一來它確實是配置解析過程的一個部分,二來我們可以檢驗下我們對各種 conf 結構儲存的位置是否真正的清晰明瞭。
首先我們瞭解下 merge 的背景:
- 所謂 merge 操作,就是合併內外層的配置。大體原則是:如果內層沒有配置,那麼以外層為準,如果都沒有配置,那麼就用預設值;
- NGX_CORE_MODULE 模組的 ctx(ngx_core_module_t)是沒有 merge 操作的,所以像 http 塊這一層的配置是不需要和上一層去 merge 的,想想也明白為什麼,http 哪來的上一層呢?
- NGX_HTTP_MODULE 模組的 ctx(ngx_http_module_t)是有 merge 操作的,但是僅僅有 merge_srv_conf 和 merge_loc_conf,同理對 main 層不需要 merge;
- merge 操作發生的時機是在 ngx_http_block函式中(即 http 塊解析函式),在遞迴呼叫 ngx_conf_parse 之後。這是為了讓 http 塊之內所有的指令都解析結束,然後再去做 merge 操作;
- 不同層級塊的邏輯關係,基本上都是放在 ngx_http_core_module 這個模組的不同級別的 conf 中,在 merge 中會頻繁用到。
來看看 ngx_http_block 中的一段:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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; /*
init http{} main_conf's */ if
(module->init_main_conf) { rv
= module->init_main_conf(cf, ctx->main_conf[mi]); if
(rv != NGX_CONF_OK) { goto
failed; }
}
rv
= ngx_http_merge_servers(cf, cmcf, module, mi); if
(rv != NGX_CONF_OK) { goto
failed; }
} |
再明顯不過了,init 了 main,merge 了 server。 這裡的處理流程是個 for 迴圈,每次的迭代物件是模組本身,也就是挨個模組的去處理 merge。
在進入 ngx_http_merge_servers 內部分析之前,我們先確認一下這些代入的引數:
- cf 是代入的引數,但是我們真正關心的還是 cf->ctx,這個時候它其實就是 http 塊的三元組(代入 ngx_http_block 的時候還不是,但是在函式中賦值了);
- cmcf 這個 http 塊的 ngx_http_core_module 的 main_conf 結構;
- module 是個迴圈獲取的,代表當前模組;
- mi 就是當前模組在 NGX_HTTP_MODULE 模組中的 index;
這裡很奇怪的就是並沒有明確指出 merge loc_conf 的地方,我們推測它是在 ngx_http_merge_servers 中實現的,那麼我們具體的分析下它的程式碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
static
char
* ngx_http_merge_servers(ngx_conf_t
*cf, ngx_http_core_main_conf_t *cmcf, ngx_http_module_t
*module, ngx_uint_t ctx_index) { char
*rv; ngx_uint_t
s; ngx_http_conf_ctx_t
*ctx, saved; ngx_http_core_loc_conf_t
*clcf; ngx_http_core_srv_conf_t
**cscfp; cscfp
= cmcf->servers.elts; /*
cmcf 裡儲存 server 的陣列,把這個陣列的地址賦值給 cscfp。*/ ctx
= (ngx_http_conf_ctx_t *) cf->ctx; saved
= *ctx; /*
這裡做了一個儲存的操作,在下面的程式碼中要改變 ctx 中的值,並且同時使用原始的 ctx。*/ rv
= NGX_CONF_OK; for
(s = 0; s < cmcf->servers.nelts; s++) { /*
這裡是遍歷所有的 server,挨個處理 merge 它的 srv_conf。*/ /*
merge the server{}s' srv_conf's */ ctx->srv_conf
= cscfp[s]->ctx->srv_conf; /*
改變 cf->ctx 的 srv_conf,換成當前 server 塊對應的 srv_conf。*/ if
(module->merge_srv_conf) { rv
= module->merge_srv_conf(cf, saved.srv_conf[ctx_index], cscfp[s]->ctx->srv_conf[ctx_index]); /*
這裡就很明朗了,saved 就是 cf->ctx 的內容,那麼它就是 http 塊的三元組了,這裡代入 *
的第二個引數也就是 http 塊中三元組所儲存模組對應的 srv_conf。cscfp[s] 代表對應的 *
server,而它的 ctx 也就是在解析那個 server 塊的時候建立的三元組,那麼第三個引數其 *
實就是那個 server 塊自己所儲存的模組對應 srv_conf 結構。所以這裡,第二個引數是 *
parent,第三個引數是 child。*/ if
(rv != NGX_CONF_OK) { goto
failed; } } if
(module->merge_loc_conf) { /*
merge the server{}'s loc_conf */ ctx->loc_conf
= cscfp[s]->ctx->loc_conf; /*
改變 cf->ctx 的 loc_conf,換成當前 server 塊對應的 loc_conf。*/ rv
= module->merge_loc_conf(cf, saved.loc_conf[ctx_index], cscfp[s]->ctx->loc_conf[ctx_index]); /*
在這裡和我們上一步分析的 merge_srv_conf 如出一轍,只是 srv_conf 換成了 loc_conf, *
別忘了在 http 塊和 server 塊也要儲存 loc_conf 的。*/ if
(rv != NGX_CONF_OK) { goto
failed; } /*
merge the locations{}' loc_conf's */ clcf
= cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index]; rv
= ngx_http_merge_locations(cf, clcf->locations, cscfp[s]->ctx->loc_conf, module,
ctx_index); /*
這裡終於要呼叫到下一層了,這個函式看名字就可以知道,是 merge 各個 location 的。*/ if
(rv != NGX_CONF_OK) { goto
failed; } } } failed: *ctx
= saved; /*
恢復了 ctx 內容,別忘了這個時候 ctx 表示的是 http 塊建立的三元組。*/ return
rv; } |
在這個函式中分別對 srv_conf 和 loc_conf 做了 merge 操作,用 http 塊建立的 srv_conf 和 loc_conf 作為外層,用 server 塊建立的 srv_conf 和 loc_conf 作為內層。但是考慮到這不是終結,還需要繼續深入到 location 中繼續 merge,所以要把 cf->ctx 的內容做修改,不斷的用內層內容覆蓋再繼續去 merge,所以重點關注下 cf->ctx 內容的改變。當然在函式最後結束的時候,還需要恢復 cf->ctx 的值。
那麼下一步我們需要分析一下 ngx_http_merge_locations 函式,這個函式負責各個 location 塊的 merge。同樣的,在分析它之前,先想想上下文環境:
- 在 ngx_http_merge_servers 中 cf->ctx 中的 srv_conf 和 loc_conf 都被改成當前 server 對應的 conf 結構了,明顯是為了進一步 merge 做準備;
- 從 ngx_http_merge_servers 中進入 ngx_http_merge_locations 的 clcf 就是從 server 塊 ngx_http_core_module 的 loc_conf;
- location 是可以巢狀的,那麼巢狀關係的內外層 location 是否也會 merge 呢?
我們來分析 ngx_http_merge_locations 的程式碼吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
static
char
* ngx_http_merge_locations(ngx_conf_t
*cf, ngx_queue_t *locations, void
**loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index) { char
*rv; ngx_queue_t
*q; ngx_http_conf_ctx_t
*ctx, saved; ngx_http_core_loc_conf_t
*clcf; ngx_http_location_queue_t
*lq; if
(locations == NULL) { return
NGX_CONF_OK; } ctx
= (ngx_http_conf_ctx_t *) cf->ctx; /*
這裡程式碼看似和 ngx_http_merge_servers 類似,但是差別在於,這個時候 cf->ctx 內 *
相對應的 srv_conf 和 loc_conf 內容已經被改變為外層的對應 conf。在 *
ngx_http_merge_servers 中的相關程式碼我們已經分析過了,在這個函式中同樣有類似的 *
程式碼。*/ saved
= *ctx; for
(q = ngx_queue_head(locations); q
!= ngx_queue_sentinel(locations); q
= ngx_queue_next(q)) { /*
遍歷一層的 location,挨個處理 merge。*/ lq
= (ngx_http_location_queue_t *) q; clcf
= lq->exact ? lq->exact : lq->inclusive; ctx->loc_conf
= clcf->loc_conf; /*
改變 cf->ctx 的 loc_conf,換成當前 server 塊對應的 loc_conf。*/ rv
= module->merge_loc_conf(cf, loc_conf[ctx_index], clcf->loc_conf[ctx_index]); /*
這裡是呼叫各個模組的 merge_loc_conf,在這裡 loc_conf 是上一層代入的,例如在 merge *
server 的時候,它是對應 server 塊的 loc_conf 陣列。在下面的程式碼中,遞迴呼叫 *
ngx_http_merge_locations,代入的 loc_conf 就是這一層塊的 loc_conf 陣列,所以這裡的 *
loc_conf 其實就代表外層塊的 loc_conf 陣列。而 clcf 是很明顯的,是由 locations 佇列 *
遍歷產生的,也就是代表當前的 location 塊。所以這裡,第二個引數是 parent,第三個參 *
數是 child。*/ if
(rv != NGX_CONF_OK) { return
rv; } rv
= ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf, module,
ctx_index); /*
這裡遞迴呼叫了 ngx_http_merge_locations,巢狀 location 的 merge 操作也可以成功解決 *
了,唯一值得注意的就是那些代入的引數,因為進入一層,所以對應的 locations 和 *
loc_conf 也更進了一層。*/ if
(rv != NGX_CONF_OK) { return
rv; } } *ctx
= saved; return
NGX_CONF_OK; } |
這裡唯一值得注意的就是那個遞迴呼叫,這裡成功的解決了 location 巢狀的 merge 問題。
總結一下重點吧:
- 在 ngx_http_block 中呼叫 ngx_http_merge_servers 去 merge 對應的 srv_conf;
- 在 ngx_http_merge_servers 和 ngx_http_merge_locations 中呼叫 ngx_http_merge_locations 去 merge 對應的 loc_conf;
- 在記憶體上,注意內外層關係,仔細閱讀程式碼,看看內外級關係的記憶體是如何處理的;
- cf->ctx 在一層層的 merge 中也起到了至關重要的作用,到了內層 merge 的時候,它存的總是邏輯上的直接外層三元組。
相關文章
- Nginx全配置解析Nginx
- Nginx(六):配置解析之location解析Nginx
- debian11 hexo+nginx 配置httpsHexoNginxHTTP
- Nginx Rewrite實際應用配置解析Nginx
- 11g rac監聽配置解析
- 從零手寫實現 nginx-13-nginx.conf 配置例子解釋 + nginx 配置檔案要如何解析?Nginx
- MogDB/openGauss中merge的語法解析
- nginx配置Nginx
- 配置nginxNginx
- Nginx 配置Nginx
- nginx之 nginx限流配置Nginx
- 【Nginx】Referer配置Nginx
- 配置nginx支援Nginx
- Nginx部署配置Nginx
- Nginx - 最小配置Nginx
- nginx常用配置Nginx
- Docker配置nginxDockerNginx
- NGINX基本配置Nginx
- Nginx 配置域名Nginx
- Nginx Location 配置Nginx
- nginx 配置使用Nginx
- Nginx配置HTTPSNginxHTTP
- nginx代理配置Nginx
- 7、Lunix下nginx反向代理伺服器域名解析配置實操Nginx伺服器
- nginx配置詳解Nginx
- Nginx 配置檔案Nginx
- Nginx負載配置Nginx負載
- nginx常用配置教程。Nginx
- NGINX配置SSL支援Nginx
- nginx配置檔案Nginx
- Nginx日誌配置Nginx
- Nginx安全配置研究Nginx
- 二、Nginx 配置 httpsNginxHTTP
- MAMP PRO Nginx配置Nginx
- nginx配置轉發Nginx
- Nginx常見配置Nginx
- [譯]如何配置NginxNginx
- nginx 限流配置Nginx
- nginx配置練習Nginx