溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析

發(fā)布時間:2022-04-29 16:41:20 來源:億速云 閱讀:255 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析”吧!

        首先我來大概的介紹一下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個帶 “~” 號前綴的為需要正則匹配的location,nginx在進(jìn)行url解析時對這5種不同類型的location具有不同的優(yōu)先級規(guī)則,大致的規(guī)則如下:

1,字符串精確匹配到一個帶 “=” 號前綴的location,則停止,且使用這個location的配置;

2,字符串匹配剩下的非正則和非特殊location,如果匹配到某個帶 "^~" 前綴的location,則停止;

3,正則匹配,匹配順序?yàn)閘ocation在配置文件中出現(xiàn)的順序。如果匹配到某個正則location,則停止,并使用這個location的配置;否則,使用步驟2中得到的具有最大字符串匹配的location配置。

       例如,對下面的請求有:

1, /   ->   精確匹配到第1個location,匹配停止,使用configuration a
2,/some/other/url    ->  首先前綴部分字符串匹配到了第2個location,然后進(jìn)行正則匹配,顯然沒有匹配上,則使用第2個location的配置configurationb
3,/images /1.jpg  ->  首先前綴部分字符串匹配到了第2個location,但是接著對第3個location也前綴匹配上了,而且這時已經(jīng)是配置文件里面對這個url的最大字符串匹配了,并且location帶有 "^~" 前綴,則不再進(jìn)行正則匹配,最終使用configuration c
4,/some/other/path/to/1.jpg  -> 首先前綴部分同樣字符串匹配到了第2個location,然后進(jìn)行正則匹配,這時正則匹配成功,則使用congifuration d

      nginx的url匹配規(guī)則實(shí)際上有點(diǎn)不妥,大部分情況下一個url必須先進(jìn)行字符串匹配,然后再做正則匹配,但是實(shí)際上如果先做正則匹配,沒有匹配上再 做字符串匹配,在很多情況下可以節(jié)省掉做字符串匹配的時間。不管怎樣,先來看一下nginx源碼里面的實(shí)現(xiàn),在介紹匹配location過程之前,先來介 紹一下nginx里面對location的組織方式,實(shí)際上在配置解析階段,nginx將字符串匹配的location和正則匹配的location分別 存儲在http core模塊的loc配置ngx_http_core_loc_conf_t結(jié)構(gòu)的下面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只是一個數(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; 
    } 
  }

        經(jīng)過配置的讀取之后,所有server都被保存在http core模塊的main配置中的servers數(shù)組中,而每個server里面的location都被按配置中出現(xiàn)的順序保存在http core模塊的loc配置的locations隊(duì)列中,上面的代碼中先對每個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) (排序好的,如果某個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; 
}

          
       上面的步驟將正則匹配的location保存好了,location tree的建立在ngx_http_init_static_location_trees中進(jìn)行:

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,也就是如果某個exact_match的location名字和普通字符串匹配的location名字相同的話, 
    就將它們合到一個節(jié)點(diǎn)中,分別保存在節(jié)點(diǎn)的exact和inclusive下,這一步的目的實(shí)際是去重,為后面的建立排序樹做準(zhǔn)備 */ 
  if (ngx_http_join_exact_locations(cf, locations) != ngx_ok) { 
    return ngx_error; 
  } 
  /* 遞歸每個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; 
}

        經(jīng)過ngx_http_init_location()函數(shù)處理之后,locations隊(duì)列已經(jīng)是排好序的了,建立三叉樹的過程的主要工作都在ngx_http_create_locations_list()和ngx_http_create_locations_tree()中完成,這2個 函數(shù)都是遞歸函數(shù),第1個函數(shù)遞歸locations隊(duì)列中的每個節(jié)點(diǎn),得到以當(dāng)前節(jié)點(diǎn)的名字為前綴的location,并保存在當(dāng)前節(jié)點(diǎn)的list字段 下,例如,對下列l(wèi)ocation:

location /xyz { 
 
} 
 
location = /xyz { 
 
} 
location /xyza { 
 
} 
 
location /xyzab { 
 
} 
location /xyzb { 
 
} 
location /abc { 
 
} 
location /efg { 
 
} 
location /efgaa { 
 
}

        排序的結(jié)果為/abc  /efg   /efgaa  =/xyz  /xyz  /xyza /xyzab /xyzb,去重后結(jié)果為 /abc  /efg   /efgaa   /xyz  /xyza /xyzab/xyzb,ngx_http_create_locations_list()執(zhí)行后的結(jié)果為:

Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析

 最后,來看下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個字段, location tree實(shí)際上是一個三叉的字符串排序樹,而且這里如果某個節(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)的名字不用保存公共前綴,而且查找的時候,如果是轉(zhuǎn)向tree節(jié)點(diǎn)的話,也是不需要再比較父節(jié)點(diǎn)的那段字符串了。
         ngx_http_create_locations_tree()函數(shù)寫的很清晰,它有一個參數(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)的完整名字):

Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析

ps:關(guān)于 location modifier
1. =
這會完全匹配指定的 pattern ,且這里的 pattern 被限制成簡單的字符串,也就是說這里不能使用正則表達(dá)式。

example:
server {
  server_name jb51.net;
  location = /abcd {
  […]
  }
}

匹配情況:

  http://jb51.net/abcd    # 正好完全匹配
  http://jb51.net/abcd    # 如果運(yùn)行 nginx server 的系統(tǒng)本身對大小寫不敏感,比如 windows ,那么也匹配
  http://jb51.net/abcd?param1&para;m2  # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1&para;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)本身對大小寫不敏感,比如 windows ,那么也匹配
  http://jb51.net/abcd?param1&para;m2  # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1&para;m2
  http://jb51.net/abcd/  # 末尾存在反斜杠(trailing slash)也屬于匹配范圍內(nèi)
  http://jb51.net/abcde  # 仍然匹配,因?yàn)?nbsp;uri 是以 pattern 開頭的

3. ~
這個 location modifier 對大小寫敏感,且 pattern 須是正則表達(dá)式

example:
server {
  server_name jb51.net;
  location ~ ^/abcd$ {
  […]
  }
}

匹配情況:

  http://jb51.net/abcd    # 完全匹配
  http://jb51.net/abcd    # 不匹配,~ 對大小寫是敏感的
  http://jb51.net/abcd?param1&para;m2  # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1&para;m2
  http://jb51.net/abcd/  # 不匹配,因?yàn)槟┪泊嬖诜葱备埽╰railing slash),并不匹配正則表達(dá)式 ^/abcd$
  http://jb51.net/abcde  # 不匹配正則表達(dá)式 ^/abcd$

注意:對于一些對大小寫不敏感的系統(tǒng),比如 windows ,~ 和 ~* 都是不起作用的,這主要是操作系統(tǒng)的原因。

4. ~*
與 ~ 類似,但這個 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&para;m2  # 忽略查詢串參數(shù)(query string arguments),這里就是 /abcd 后面的 ?param1&para;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. @
用于定義一個 location 塊,且該塊不能被外部 client 所訪問,只能被 nginx 內(nèi)部配置指令所訪問,比如 try_files or error_page

感謝各位的閱讀,以上就是“Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對Nginx服務(wù)器中l(wèi)ocation配置實(shí)例分析這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI