Nginx伺服器中l(wèi)ocation設(shè)定實(shí)例分析
May 24, 2023 pm 02:05 PM??????? 首先我來大概的介紹一下location的種類和匹配規(guī)則,以nginx wiki的例子做說明:
location = / { # matches the query / only. [ configuration a ] } location / { # matches any query, since all queries begin with /, but regular # expressions and any longer conventional blocks will be # matched first. [ configuration b ] } location ^~ /images/ { # matches any query beginning with /images/ and halts searching, # so regular expressions will not be checked. [ configuration c ] } location ~* \.(gif|jpg|jpeg)$ { # matches any request ending in gif, jpg, or jpeg. however, all # requests to the /images/ directory will be handled by # configuration c. [ configuration d ] } location @named { # such locations are not used during normal processing of requests, # they are intended only to process internally redirected requests (for example error_page, try_files). [ configuration e ] }
??????? 可以看到上面的例子中有5種不同類型的location其中第4個(gè)帶有「~」 號(hào)前綴的為需要正規(guī)匹配的location,nginx在進(jìn)行url解析時(shí)對(duì)這5種不同類型的location具有不同的優(yōu)先權(quán)規(guī)則,大致的規(guī)則如下:
1,字串精確匹配到一個(gè)帶有「=」 號(hào)前綴的location,則停止,且使用這個(gè)location的配置;
2,字串匹配剩下的非正則和非特殊location,如果匹配到某個(gè)帶"^~" 前綴的location,則停止;
3,正則匹配,匹配順序?yàn)閘ocation在配置文件中出現(xiàn)的順序。如果匹配到某個(gè)正則location,則停止,並使用這個(gè)location的配置;否則,使用步驟2中得到的具有最大字串匹配的location配置。
?????? 例如,對(duì)下面的請(qǐng)求有:
#1, /?? ->?? 精確配對(duì)到第1個(gè)location,配對(duì)停止,使用configuration a
2,/some/other/url ->? 首先前綴部分字串匹配到了第2個(gè)location,然後進(jìn)行正規(guī)匹配,顯然沒有匹配上,則使用第2個(gè)location的配置configurationb
3,/images /1.jpg? ->? 首先前綴部分字串匹配到了第2個(gè)location,但是接著對(duì)第3個(gè)location也前綴匹配上了,而且這時(shí)已經(jīng)是配置文件裡面對(duì)這個(gè)url的最大字符串匹配了,並且location帶有"^~" 前綴,則不再進(jìn)行正規(guī)匹配,最終使用configuration c
4,/some/other/path/to/1.jpg? -> 首先前綴部分同樣字符串匹配到了第2個(gè)location,然後進(jìn)行正則匹配,這時(shí)正則匹配成功,則使用congifuration d
????? nginx的url匹配規(guī)則實(shí)際上有點(diǎn)不妥,大部分情況下一個(gè)url必須先進(jìn)行字符串匹配,然後再做正則匹配,但是實(shí)際上如果先做正規(guī)匹配,沒有匹配上再做字符串匹配,在很多情況下可以節(jié)省掉做字符串匹配的時(shí)間。不管怎樣,先來介紹一下nginx源碼裡面的實(shí)現(xiàn),在介紹匹配location過程之前,先來介紹一下nginx裡面對(duì)location的組織方式,實(shí)際上在配置解析階段,nginx將字符串匹配的location和正則匹配的location分別儲(chǔ)存在http core模組的loc配置ngx_http_core_loc_conf_t結(jié)構(gòu)的下面2個(gè)欄位:
ngx_http_location_tree_node_t *static_locations; (ngx_pcre) ngx_http_core_loc_conf_t **regex_locations; if
從這2個(gè)欄位的型別可以看出,字串相符的location被組織成了一個(gè)location tree,而正規(guī)符合的location只是一個(gè)數(shù)組,
location tree和regex_locations數(shù)組建立過程在ngx_http_block中: /* create location trees */ 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) { return ngx_conf_error; } if (ngx_http_init_static_location_trees(cf, clcf) != ngx_ok) { return ngx_conf_error; } }
??????? 配置的讀取之後,所有server都被保存在http core模組的main配置中的servers數(shù)組中,而每個(gè)server裡面的location都被按配置中出現(xiàn)的順序保存在http core模組的loc配置的locations佇列中,上面的程式碼中先對(duì)每個(gè)server的location進(jìn)行排序和分類處理,這一步發(fā)生在ngx_http_init_location()函數(shù)中:
static ngx_int_t ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf) { ... locations = pclcf->locations; ... /* 按照類型排序location,排序完后的隊(duì)列: (exact_match 或 inclusive) (排序好的,如果某個(gè)exact_match名字和inclusive location相同,exact_match排在前面) | regex(未排序)| named(排序好的) | noname(未排序)*/ ngx_queue_sort(locations, ngx_http_cmp_locations); named = null; n = 0; #if (ngx_pcre) regex = null; r = 0; #endif for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; /* 由于可能存在nested location,也就是location里面嵌套的location,這里需要遞歸的處理一下當(dāng)前l(fā)ocation下面的nested location */ if (ngx_http_init_locations(cf, null, clcf) != ngx_ok) { return ngx_error; } #if (ngx_pcre) if (clcf->regex) { r++; if (regex == null) { regex = q; } continue; } #endif if (clcf->named) { n++; if (named == null) { named = q; } continue; } if (clcf->noname) { break; } } if (q != ngx_queue_sentinel(locations)) { ngx_queue_split(locations, q, &tail); } /* 如果有named location,將它們保存在所屬server的named_locations數(shù)組中 */ if (named) { clcfp = ngx_palloc(cf->pool, (n + 1) * sizeof(ngx_http_core_loc_conf_t **)); if (clcfp == null) { return ngx_error; } cscf->named_locations = clcfp; for (q = named; q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; *(clcfp++) = lq->exact; } *clcfp = null; ngx_queue_split(locations, named, &tail); } #if (ngx_pcre) /* 如果有正則匹配location,將它們保存在所屬server的http core模塊的loc配置的regex_locations 數(shù)組中, 這里和named location保存位置不同的原因是由于named location只能存在server里面,而regex location可以作為nested location */ if (regex) { clcfp = ngx_palloc(cf->pool, (r + 1) * sizeof(ngx_http_core_loc_conf_t **)); if (clcfp == null) { return ngx_error; } pclcf->regex_locations = clcfp; for (q = regex; q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; *(clcfp++) = lq->exact; } *clcfp = null; ngx_queue_split(locations, regex, &tail); } #endif return ngx_ok; }
??????????
卷[?????之後,locations佇列已經(jīng)是排好序的了,建立三叉樹的過程的主要工作都在ngx_http_create_locations_list()和ngx_http_create_locations_tree()中完成,這2個(gè)函數(shù)都是遞歸函數(shù),第1個(gè)函數(shù)遞歸locations佇列中的每個(gè)節(jié)點(diǎn),得到以目前節(jié)點(diǎn)的名字為前綴的location,並保存在目前節(jié)點(diǎn)的list欄位下,例如,對(duì)下列l(wèi)ocation:
static ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf, ngx_http_core_loc_conf_t *pclcf) { ngx_queue_t *q, *locations; ngx_http_core_loc_conf_t *clcf; ngx_http_location_queue_t *lq; locations = pclcf->locations; if (locations == null) { return ngx_ok; } if (ngx_queue_empty(locations)) { return ngx_ok; } /* 這里也是由于nested location,需要遞歸一下 */ for (q = ngx_queue_head(locations); q != ngx_queue_sentinel(locations); q = ngx_queue_next(q)) { lq = (ngx_http_location_queue_t *) q; clcf = lq->exact ? lq->exact : lq->inclusive; if (ngx_http_init_static_location_trees(cf, clcf) != ngx_ok) { return ngx_error; } } /* join隊(duì)列中名字相同的inclusive和exact類型location,也就是如果某個(gè)exact_match的location名字和普通字符串匹配的location名字相同的話, 就將它們合到一個(gè)節(jié)點(diǎn)中,分別保存在節(jié)點(diǎn)的exact和inclusive下,這一步的目的實(shí)際是去重,為后面的建立排序樹做準(zhǔn)備 */ if (ngx_http_join_exact_locations(cf, locations) != ngx_ok) { return ngx_error; } /* 遞歸每個(gè)location節(jié)點(diǎn),得到當(dāng)前節(jié)點(diǎn)的名字為其前綴的location的列表,保存在當(dāng)前節(jié)點(diǎn)的list字段下 */ ngx_http_create_locations_list(locations, ngx_queue_head(locations)); /* 遞歸建立location三叉排序樹 */ pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0); if (pclcf->static_locations == null) { return ngx_error; } return ngx_ok; }
??????? 排序的結(jié)果為/abc? /efg?? /efgaa? 排序的結(jié)果為/abc? /efg?? /efgaa? =/zz?xyz? /xyza /xyzab /xyzb,去重後結(jié)果為/abc? /efg?? /efgaa?? /xyz? /xyza /xyzab/xyzb,ngx_http_create_locations_list()執(zhí)行後的結(jié)果為:
?最後,來看ngx_http_create_locations_tree函數(shù):
static ngx_http_location_tree_node_t * ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations, size_t prefix) { ... /* 根節(jié)點(diǎn)為locations隊(duì)列的中間節(jié)點(diǎn) */ q = ngx_queue_middle(locations); lq = (ngx_http_location_queue_t *) q; len = lq->name->len - prefix; node = ngx_palloc(cf->pool, offsetof(ngx_http_location_tree_node_t, name) + len); if (node == null) { return null; } node->left = null; node->right = null; node->tree = null; node->exact = lq->exact; node->inclusive = lq->inclusive; node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect) || (lq->inclusive && lq->inclusive->auto_redirect)); node->len = (u_char) len; ngx_memcpy(node->name, &lq->name->data[prefix], len); /* 從中間節(jié)點(diǎn)開始斷開 */ ngx_queue_split(locations, q, &tail); if (ngx_queue_empty(locations)) { /* * ngx_queue_split() insures that if left part is empty, * then right one is empty too */ goto inclusive; } /* 從locations左半部分得到左子樹 */ node->left = ngx_http_create_locations_tree(cf, locations, prefix); if (node->left == null) { return null; } ngx_queue_remove(q); if (ngx_queue_empty(&tail)) { goto inclusive; } /* 從locations右半部分得到右子樹 */ node->right = ngx_http_create_locations_tree(cf, &tail, prefix); if (node->right == null) { return null; } inclusive: if (ngx_queue_empty(&lq->list)) { return node; } /* 從list隊(duì)列得到tree子樹 */ node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len); if (node->tree == null) { return null; } return node; } location tree節(jié)點(diǎn)的ngx_http_location_tree_node_s結(jié)構(gòu): struct ngx_http_location_tree_node_s { ngx_http_location_tree_node_t *left; ngx_http_location_tree_node_t *right; ngx_http_location_tree_node_t *tree; ngx_http_core_loc_conf_t *exact; ngx_http_core_loc_conf_t *inclusive; u_char auto_redirect; u_char len; u_char name[1]; };
location tree結(jié)構(gòu)用到的是left,right,tree 這3個(gè)字段, location tree實(shí)際上是一個(gè)三叉的字符串排序樹,而且這里如果某個(gè)節(jié)點(diǎn)只考慮左,右子樹,它是一顆平衡樹,它的建立過程有點(diǎn)類似于一顆平衡排序二叉樹的建立過程,先排序再用二分查找找到的節(jié)點(diǎn)順序插入,ngx_http_location_tree_node_s的tree節(jié)點(diǎn)也是一顆平衡排序樹,它是用該節(jié)點(diǎn)由ngx_http_create_locations_list()得到的list建立的,也就是該節(jié)點(diǎn)的名字是它的tree子樹里面的所有節(jié)點(diǎn)名字的前綴,所以tree子樹里面的所有節(jié)點(diǎn)的名字不用保存公共前綴,而且查找的時(shí)候,如果是轉(zhuǎn)向tree節(jié)點(diǎn)的話,也是不需要再比較父節(jié)點(diǎn)的那段字符串了。
ngx_http_create_locations_tree()函數(shù)寫的很清晰,它有一個(gè)參數(shù)是隊(duì)列l(wèi)ocations,它返回一顆三叉樹,根節(jié)點(diǎn)為locations的中間節(jié)點(diǎn),其左子樹為locations隊(duì)列的左半部分建立的location tree,右子樹為location隊(duì)列的右半部分建立的tree,tree節(jié)點(diǎn)為該根節(jié)點(diǎn)的list隊(duì)列建立的tree。
最終建立的location tree如下(為了方便閱讀,圖中列出了tree節(jié)點(diǎn)的完整名字):
ps:關(guān)于 location modifier
1. =
這會(huì)完全匹配指定的 pattern ,且這里的 pattern 被限制成簡(jiǎn)單的字符串,也就是說這里不能使用正則表達(dá)式。
example: server { server_name jb51.net; location = /abcd { […] } }
匹配情況:
http://jb51.net/abcd # 正好完全匹配 http://jb51.net/abcd # 如果運(yùn)行 nginx server 的系統(tǒng)本身對(duì)大小寫不敏感,比如 windows ,那么也匹配 http://jb51.net/abcd?param1¶m2 # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因?yàn)槟┪泊嬖诜葱备埽╰railing slash),nginx 不認(rèn)為這種情況是完全匹配 http://jb51.net/abcde # 不匹配,因?yàn)椴皇峭耆ヅ?/pre>2. (none)
可以不寫 location modifier ,nginx 仍然能去匹配 pattern 。這種情況下,匹配那些以指定的 patern 開頭的 uri,注意這里的 uri 只能是普通字符串,不能使用正則表達(dá)式。example: server { server_name jb51.net; location /abcd { […] } }匹配情況:
http://jb51.net/abcd # 正好完全匹配 http://jb51.net/abcd # 如果運(yùn)行 nginx server 的系統(tǒng)本身對(duì)大小寫不敏感,比如 windows ,那么也匹配 http://jb51.net/abcd?param1¶m2 # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 末尾存在反斜杠(trailing slash)也屬于匹配范圍內(nèi) http://jb51.net/abcde # 仍然匹配,因?yàn)?uri 是以 pattern 開頭的3. ~
這個(gè) location modifier 對(duì)大小寫敏感,且 pattern 須是正則表達(dá)式example: server { server_name jb51.net; location ~ ^/abcd$ { […] } }匹配情況:
http://jb51.net/abcd # 完全匹配 http://jb51.net/abcd # 不匹配,~ 對(duì)大小寫是敏感的 http://jb51.net/abcd?param1¶m2 # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因?yàn)槟┪泊嬖诜葱备埽╰railing slash),并不匹配正則表達(dá)式 ^/abcd$ http://jb51.net/abcde # 不匹配正則表達(dá)式 ^/abcd$注意:對(duì)于一些對(duì)大小寫不敏感的系統(tǒng),比如 windows ,~ 和 ~* 都是不起作用的,這主要是操作系統(tǒng)的原因。
4. ~*
與 ~ 類似,但這個(gè) location modifier 不區(qū)分大小寫,pattern 須是正則表達(dá)式example: server { server_name jb51.net; location ~* ^/abcd$ { […] } }匹配情況:
http://jb51.net/abcd # 完全匹配 http://jb51.net/abcd # 匹配,這就是它不區(qū)分大小寫的特性 http://jb51.net/abcd?param1¶m2 # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因?yàn)槟┪泊嬖诜葱备埽╰railing slash),并不匹配正則表達(dá)式 ^/abcd$ http://jb51.net/abcde # 不匹配正則表達(dá)式 ^/abcd$5. ^~
匹配情況類似 2. (none) 的情況,以指定匹配模式開頭的 uri 被匹配,不同的是,一旦匹配成功,那么 nginx 就停止去尋找其他的 location 塊進(jìn)行匹配了(與 location 匹配順序有關(guān))6. @
用于定義一個(gè) location 塊,且該塊不能被外部 client 所訪問,只能被 nginx 內(nèi)部配置指令所訪問,比如 try_files or error_page以上是Nginx伺服器中l(wèi)ocation設(shè)定實(shí)例分析的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣圖片

Undresser.AI Undress
人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強(qiáng)大的PHP整合開發(fā)環(huán)境

Dreamweaver CS6
視覺化網(wǎng)頁開發(fā)工具

SublimeText3 Mac版
神級(jí)程式碼編輯軟體(SublimeText3)

熱門話題

Docker 容器啟動(dòng)步驟:拉取容器鏡像:運(yùn)行 "docker pull [鏡像名稱]"。創(chuàng)建容器:使用 "docker create [選項(xiàng)] [鏡像名稱] [命令和參數(shù)]"。啟動(dòng)容器:執(zhí)行 "docker start [容器名稱或 ID]"。檢查容器狀態(tài):通過 "docker ps" 驗(yàn)證容器是否正在運(yùn)行。

可以通過以下步驟查詢 Docker 容器名稱:列出所有容器(docker ps)。篩選容器列表(使用 grep 命令)。獲取容器名稱(位於 "NAMES" 列中)。

確認(rèn) Nginx 是否啟動(dòng)的方法:1. 使用命令行:systemctl status nginx(Linux/Unix)、netstat -ano | findstr 80(Windows);2. 檢查端口 80 是否開放;3. 查看系統(tǒng)日誌中 Nginx 啟動(dòng)消息;4. 使用第三方工具,如 Nagios、Zabbix、Icinga。

在 Docker 中創(chuàng)建容器: 1. 拉取鏡像: docker pull [鏡像名] 2. 創(chuàng)建容器: docker run [選項(xiàng)] [鏡像名] [命令] 3. 啟動(dòng)容器: docker start [容器名]

問題:如何啟動(dòng) Nginx?答案:安裝 Nginx啟動(dòng) Nginx驗(yàn)證 Nginx 是否已啟動(dòng)探索其他啟動(dòng)選項(xiàng)自動(dòng)啟動(dòng) Nginx

NGINX和Apache各有優(yōu)劣,適合不同場(chǎng)景。 1.NGINX適合高並發(fā)和低資源消耗場(chǎng)景。 2.Apache適合需要復(fù)雜配置和豐富模塊的場(chǎng)景。通過比較它們的核心特性、性能差異和最佳實(shí)踐,可以幫助你選擇最適合需求的服務(wù)器軟件。

提升CentOS系統(tǒng)下PhpStorm性能的實(shí)用技巧本文提供多種方法,幫助您優(yōu)化CentOS系統(tǒng)中PhpStorm的性能,從而提高開發(fā)效率。在實(shí)施任何優(yōu)化措施前,請(qǐng)務(wù)必備份重要數(shù)據(jù)并在測(cè)試環(huán)境中驗(yàn)證效果。一、系統(tǒng)級(jí)優(yōu)化精簡(jiǎn)系統(tǒng)服務(wù):禁用不必要的系統(tǒng)服務(wù)和守護(hù)進(jìn)程,減少系統(tǒng)資源占用。無界面模式:如果不需要圖形界面,切換到無界面模式可顯著節(jié)省資源。卸載冗余軟件:移除不再使用的軟件包和服務(wù),釋放系統(tǒng)資源。二、PHP配置優(yōu)化啟用OPcache:安裝并配置OPcache擴(kuò)展,顯

NGINX和Apache都是強(qiáng)大的Web服務(wù)器,各自在性能、可擴(kuò)展性和效率上有獨(dú)特的優(yōu)勢(shì)和不足。 1)NGINX在處理靜態(tài)內(nèi)容和反向代理時(shí)表現(xiàn)出色,適合高並發(fā)場(chǎng)景。 2)Apache在處理動(dòng)態(tài)內(nèi)容時(shí)表現(xiàn)更好,適合需要豐富模塊支持的項(xiàng)目。選擇服務(wù)器應(yīng)根據(jù)項(xiàng)目需求和場(chǎng)景來決定。
