Nginx伺服器中location設定實例分析
首先我來大概的介紹一下location的種類和匹配規則,以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個帶有「~」 號前綴的為需要正規匹配的location,nginx在進行url解析時對這5種不同類型的location具有不同的優先權規則,大致的規則如下:
1,字串精確匹配到一個帶有「=」 號前綴的location,則停止,且使用這個location的配置;
2,字串匹配剩下的非正則和非特殊location,如果匹配到某個帶"^~" 前綴的location,則停止;
3,正則匹配,匹配順序為location在配置文件中出現的順序。如果匹配到某個正則location,則停止,並使用這個location的配置;否則,使用步驟2中得到的具有最大字串匹配的location配置。
例如,對下面的請求有:
#1, / -> 精確配對到第1個location,配對停止,使用configuration a
2,/some/other/url -> 首先前綴部分字串匹配到了第2個location,然後進行正規匹配,顯然沒有匹配上,則使用第2個location的配置configurationb
3,/images /1.jpg -> 首先前綴部分字串匹配到了第2個location,但是接著對第3個location也前綴匹配上了,而且這時已經是配置文件裡面對這個url的最大字符串匹配了,並且location帶有"^~" 前綴,則不再進行正規匹配,最終使用configuration c
4,/some/other/path/to/1.jpg -> 首先前綴部分同樣字符串匹配到了第2個location,然後進行正則匹配,這時正則匹配成功,則使用congifuration d
nginx的url匹配規則實際上有點不妥,大部分情況下一個url必須先進行字符串匹配,然後再做正則匹配,但是實際上如果先做正規匹配,沒有匹配上再做字符串匹配,在很多情況下可以節省掉做字符串匹配的時間。不管怎樣,先來介紹一下nginx源碼裡面的實現,在介紹匹配location過程之前,先來介紹一下nginx裡面對location的組織方式,實際上在配置解析階段,nginx將字符串匹配的location和正則匹配的location分別儲存在http core模組的loc配置ngx_http_core_loc_conf_t結構的下面2個欄位:
ngx_http_location_tree_node_t *static_locations; (ngx_pcre) ngx_http_core_loc_conf_t **regex_locations; if
從這2個欄位的型別可以看出,字串相符的location被組織成了一個location tree,而正規符合的location只是一個數組,
location tree和regex_locations数组建立过程在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數組中,而每個server裡面的location都被按配置中出現的順序保存在http core模組的loc配置的locations佇列中,上面的程式碼中先對每個server的location進行排序和分類處理,這一步發生在ngx_http_init_location()函數中:
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,排序完后的队列: (exact_match 或 inclusive) (排序好的,如果某个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,这里需要递归的处理一下当前location下面的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数组中 */ 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 数组中, 这里和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佇列已經是排好序的了,建立三叉樹的過程的主要工作都在ngx_http_create_locations_list()和ngx_http_create_locations_tree()中完成,這2個函數都是遞歸函數,第1個函數遞歸locations佇列中的每個節點,得到以目前節點的名字為前綴的location,並保存在目前節點的list欄位下,例如,對下列location:
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队列中名字相同的inclusive和exact类型location,也就是如果某个exact_match的location名字和普通字符串匹配的location名字相同的话, 就将它们合到一个节点中,分别保存在节点的exact和inclusive下,这一步的目的实际是去重,为后面的建立排序树做准备 */ if (ngx_http_join_exact_locations(cf, locations) != ngx_ok) { return ngx_error; } /* 递归每个location节点,得到当前节点的名字为其前缀的location的列表,保存在当前节点的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; }
排序的結果為/abc /efg /efgaa 排序的結果為/abc /efg /efgaa =/zz xyz /xyza /xyzab /xyzb,去重後結果為/abc /efg /efgaa /xyz /xyza /xyzab/xyzb,ngx_http_create_locations_list()執行後的結果為:
最後,來看ngx_http_create_locations_tree函數:
static ngx_http_location_tree_node_t * ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations, size_t prefix) { ... /* 根节点为locations队列的中间节点 */ 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); /* 从中间节点开始断开 */ 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队列得到tree子树 */ node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len); if (node->tree == null) { return null; } return node; } location tree节点的ngx_http_location_tree_node_s结构: 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结构用到的是left,right,tree 这3个字段, location tree实际上是一个三叉的字符串排序树,而且这里如果某个节点只考虑左,右子树,它是一颗平衡树,它的建立过程有点类似于一颗平衡排序二叉树的建立过程,先排序再用二分查找找到的节点顺序插入,ngx_http_location_tree_node_s的tree节点也是一颗平衡排序树,它是用该节点由ngx_http_create_locations_list()得到的list建立的,也就是该节点的名字是它的tree子树里面的所有节点名字的前缀,所以tree子树里面的所有节点的名字不用保存公共前缀,而且查找的时候,如果是转向tree节点的话,也是不需要再比较父节点的那段字符串了。
ngx_http_create_locations_tree()函数写的很清晰,它有一个参数是队列locations,它返回一颗三叉树,根节点为locations的中间节点,其左子树为locations队列的左半部分建立的location tree,右子树为location队列的右半部分建立的tree,tree节点为该根节点的list队列建立的tree。
最终建立的location tree如下(为了方便阅读,图中列出了tree节点的完整名字):
ps:关于 location modifier
1. =
这会完全匹配指定的 pattern ,且这里的 pattern 被限制成简单的字符串,也就是说这里不能使用正则表达式。
example: server { server_name jb51.net; location = /abcd { […] } }
匹配情况:
http://jb51.net/abcd # 正好完全匹配 http://jb51.net/abcd # 如果运行 nginx server 的系统本身对大小写不敏感,比如 windows ,那么也匹配 http://jb51.net/abcd?param1¶m2 # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因为末尾存在反斜杠(trailing slash),nginx 不认为这种情况是完全匹配 http://jb51.net/abcde # 不匹配,因为不是完全匹配
2. (none)
可以不写 location modifier ,nginx 仍然能去匹配 pattern 。这种情况下,匹配那些以指定的 patern 开头的 uri,注意这里的 uri 只能是普通字符串,不能使用正则表达式。
example: server { server_name jb51.net; location /abcd { […] } }
匹配情况:
http://jb51.net/abcd # 正好完全匹配 http://jb51.net/abcd # 如果运行 nginx server 的系统本身对大小写不敏感,比如 windows ,那么也匹配 http://jb51.net/abcd?param1¶m2 # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 末尾存在反斜杠(trailing slash)也属于匹配范围内 http://jb51.net/abcde # 仍然匹配,因为 uri 是以 pattern 开头的
3. ~
这个 location modifier 对大小写敏感,且 pattern 须是正则表达式
example: server { server_name jb51.net; location ~ ^/abcd$ { […] } }
匹配情况:
http://jb51.net/abcd # 完全匹配 http://jb51.net/abcd # 不匹配,~ 对大小写是敏感的 http://jb51.net/abcd?param1¶m2 # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因为末尾存在反斜杠(trailing slash),并不匹配正则表达式 ^/abcd$ http://jb51.net/abcde # 不匹配正则表达式 ^/abcd$
注意:对于一些对大小写不敏感的系统,比如 windows ,~ 和 ~* 都是不起作用的,这主要是操作系统的原因。
4. ~*
与 ~ 类似,但这个 location modifier 不区分大小写,pattern 须是正则表达式
example: server { server_name jb51.net; location ~* ^/abcd$ { […] } }
匹配情况:
http://jb51.net/abcd # 完全匹配 http://jb51.net/abcd # 匹配,这就是它不区分大小写的特性 http://jb51.net/abcd?param1¶m2 # 忽略查询串参数(query string arguments),这里就是 /abcd 后面的 ?param1¶m2 http://jb51.net/abcd/ # 不匹配,因为末尾存在反斜杠(trailing slash),并不匹配正则表达式 ^/abcd$ http://jb51.net/abcde # 不匹配正则表达式 ^/abcd$
5. ^~
匹配情况类似 2. (none) 的情况,以指定匹配模式开头的 uri 被匹配,不同的是,一旦匹配成功,那么 nginx 就停止去寻找其他的 location 块进行匹配了(与 location 匹配顺序有关)
6. @
用于定义一个 location 块,且该块不能被外部 client 所访问,只能被 nginx 内部配置指令所访问,比如 try_files or error_page
以上是Nginx伺服器中location設定實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

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

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

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

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

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

如何在 Windows 中配置 Nginx?安裝 Nginx 並創建虛擬主機配置。修改主配置文件並包含虛擬主機配置。啟動或重新加載 Nginx。測試配置並查看網站。選擇性啟用 SSL 並配置 SSL 證書。選擇性設置防火牆允許 80 和 443 端口流量。

Docker 容器啟動步驟:拉取容器鏡像:運行 "docker pull [鏡像名稱]"。創建容器:使用 "docker create [選項] [鏡像名稱] [命令和參數]"。啟動容器:執行 "docker start [容器名稱或 ID]"。檢查容器狀態:通過 "docker ps" 驗證容器是否正在運行。

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

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

在 Docker 中創建容器: 1. 拉取鏡像: docker pull [鏡像名] 2. 創建容器: docker run [選項] [鏡像名] [命令] 3. 啟動容器: docker start [容器名]

在雲服務器上配置 Nginx 域名的方法:創建 A 記錄,指向雲服務器的公共 IP 地址。在 Nginx 配置文件中添加虛擬主機塊,指定偵聽端口、域名和網站根目錄。重啟 Nginx 以應用更改。訪問域名測試配置。其他注意事項:安裝 SSL 證書啟用 HTTPS、確保防火牆允許 80 端口流量、等待 DNS 解析生效。

可以查詢 Nginx 版本的方法有:使用 nginx -v 命令;查看 nginx.conf 文件中的 version 指令;打開 Nginx 錯誤頁,查看頁面的標題。

當 Nginx 服務器宕機時,可執行以下故障排除步驟:檢查 nginx 進程是否正在運行。查看錯誤日誌以獲取錯誤消息。檢查 nginx 配置語法正確性。確保 nginx 具有訪問文件所需的權限。檢查文件描述符打開限制。確認 nginx 正在偵聽正確的端口。添加防火牆規則以允許nginx流量。檢查反向代理設置,包括後端服務器可用性。如需進一步幫助,請聯繫技術支持。
