nginx的location優化

鄭爾多斯發表於2018-12-04

微信公眾號:鄭爾多斯
關注可瞭解更多的Nginx知識。任何問題或建議,請公眾號留言;
關注公眾號,有趣有內涵的文章第一時間送達!

簡介

本篇文章主要是分析配置檔案解析完畢之後對location的進一步優化。這一部分主要完成了靜態樹的構建,一切的目的就是為了更快捷的找到對應的location,提高http響應的速度。

程式碼分析

ngx_http_block()中有下面的一部分程式碼,如下:

 1 /* create location trees */
2    for (s = 0; s < cmcf->servers.nelts; s++) {
3      // 當前server的ctx的loc_conf[ngx_http_core_module.ctx_index]
4        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];
5
6        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {
7            return NGX_CONF_ERROR;
8        }
9
10        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {
11            return NGX_CONF_ERROR;
12        }
13    }
複製程式碼

上面的cmcfngx_http_core_modulehttp級別的ctxmain_conf陣列中對應的配置結構體,型別為ngx_http_core_main_conf_t型別。經過前面分析,我們知道它的servers欄位是一個動態陣列,儲存了當前配置檔案中的所有server指令塊對應的配置結構體。上面的程式碼就是遍歷這個server陣列,然後對每個server下面的location進行處理。下面我們看一下處理的過程:

ngx_http_init_locations

程式碼如下:

  1static ngx_int_t
2ngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,
3    ngx_http_core_loc_conf_t *pclcf)

4
{
5    ngx_uint_t                   n;
6    ngx_queue_t                 *q, *locations, *named, tail;
7    ngx_http_core_loc_conf_t    *clcf;
8    ngx_http_location_queue_t   *lq;
9    ngx_http_core_loc_conf_t   **clcfp;
10#if (NGX_PCRE)
11    ngx_uint_t                   r;
12    ngx_queue_t                 *regex;
13#endif
14
15    locations = pclcf->locations;
16
17    if (locations == NULL) {
18        return NGX_OK;
19    }
20
21    ngx_queue_sort(locations, ngx_http_cmp_locations);
22
23    named = NULL;
24    n = 0;
25#if (NGX_PCRE)
26    regex = NULL;
27    r = 0;
28#endif
29
30    for (q = ngx_queue_head(locations);
31         q != ngx_queue_sentinel(locations);
32         q = ngx_queue_next(q))
33    {
34        lq = (ngx_http_location_queue_t *) q;
35
36        clcf = lq->exact ? lq->exact : lq->inclusive;
37
38        if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {
39            return NGX_ERROR;
40        }
41
42#if (NGX_PCRE)
43
44        if (clcf->regex) {
45            r++;
46
47            if (regex == NULL) {
48                regex = q;
49            }
50
51            continue;
52        }
53
54#endif
55
56        if (clcf->named) {
57            n++;
58
59            if (named == NULL) {
60                named = q;
61            }
62
63            continue;
64        }
65
66        if (clcf->noname) {
67            break;
68        }
69    }
70
71    if (q != ngx_queue_sentinel(locations)) {
72        ngx_queue_split(locations, q, &tail);
73    }
74
75    if (named) {
76        clcfp = ngx_palloc(cf->pool,
77                           (n + 1) * sizeof(ngx_http_core_loc_conf_t *));
78        if (clcfp == NULL) {
79            return NGX_ERROR;
80        }
81
82        cscf->named_locations = clcfp;
83
84        for (q = named;
85             q != ngx_queue_sentinel(locations);
86             q = ngx_queue_next(q))
87        {
88            lq = (ngx_http_location_queue_t *) q;
89
90            *(clcfp++) = lq->exact;
91        }
92
93        *clcfp = NULL;
94
95        ngx_queue_split(locations, named, &tail);
96    }
97
98#if (NGX_PCRE)
99
100    if (regex) {
101
102        clcfp = ngx_palloc(cf->pool,
103                           (r + 1) * sizeof(ngx_http_core_loc_conf_t *));
104        if (clcfp == NULL) {
105            return NGX_ERROR;
106        }
107
108        pclcf->regex_locations = clcfp;
109
110        for (q = regex;
111             q != ngx_queue_sentinel(locations);
112             q = ngx_queue_next(q))
113        {
114            lq = (ngx_http_location_queue_t *) q;
115
116            *(clcfp++) = lq->exact;
117        }
118
119        *clcfp = NULL;
120
121        ngx_queue_split(locations, regex, &tail);
122    }
123
124#endif
125
126    return NGX_OK;
127}
複製程式碼

上面的程式碼首先呼叫ngx_queue_sort()函式對所有的location進行排序,排序之後的結果他如下:

  • 兩個比較location中的未命名location(即noname為1,rewrite_module中if指令產生的location)排到後面;
  • 如果比較的兩個location都為未命名location,則維持原次序:使用者在配置檔案裡書寫的先後順序;
  • 兩個比較location中的命名location(即named為1,@字首)排到後面;
  • 如果比較的兩個location都為命名location,則按字串大小排序,名稱字元序大的排到後面;
  • 兩個比較location中的正則匹配location(即regex欄位不為空)排到後面;
  • 如果比較的兩個location都為正則匹配location,則維持原次序:使用者在配置檔案裡書寫的先後順序;
  • 其他情況,按字串大小排序。但對於兩個location名稱相同的情況,如果存在絕對匹配location,則把它放在前

緊接著將所有屬於正規表示式和named的location分別放置到不同的地方,對於我們的例子,如下:

ngx_http_init_location之後的結構圖
ngx_http_init_location之後的結構圖

ngx_http_init_static_location_trees

這個函式主要是為了構建靜態的location三叉樹,由於我們的config檔案中的location數量太少,這裡沒有辦法進行仔細的講解,所以具體的構建過程這裡不詳細的介紹,後面文章會仔細介紹這個過程,我們這裡僅僅展示構建的最終結果:

最終形成的樹結構
最終形成的樹結構

由於我們的配置檔案只有一個location,所以形成的樹結構只有一個節點,後面我們會分析多個location的時候如何構建這棵樹。


喜歡本文的朋友們,歡迎長按下圖關注訂閱號鄭爾多斯,更多精彩內容第一時間送達

鄭爾多斯
鄭爾多斯

相關文章