10.2.3 管理location级别下的配置项

在解析srv级别配置项时,如果发现了location{}配置块,就会回调ngx_http_core_location方法(该方法属于ngx_http_core_module模块),在这个方法里则会开始解析loc级别的配置项,其流程如图10-4所示。

10.2.3 管理location级别下的配置项 - 图1

图 10-4 解析location{}配置块的流程

下面简要介绍一下图10-4中的流程:

1)在解析到location{}配置块时,仍然会像解析http块一样,先建立ngx_http_conf_ctx_t结构体,只是这里的main_conf和srv_conf都将指向所属的server块下ngx_http_conf_ctx_t结构体的main_conf和srv_conf指针数组,而loc_conf则将指向重新分配的指针数组。

2)循环调用所有HTTP模块的create_loc_conf方法,将返回的结构体指针按照模块序号ctx_index保存到上述的loc_conf指针数组中。

3)如果在location中使用了正则表达式,那么这时将调用pcre_compile方法预编译正则表达式,以提高性能。

4)第1个HTTP模块是ngx_http_core_module模块,它在create_loc_conf方法中将会生成ngx_http_core_loc_conf_t配置结构体,可以认为该结构体对应着当前解析的location块。这时会生成ngx_http_location_queue_t结构体,因为每一个ngx_http_core_loc_conf_t结构体都对应着1个ngx_http_location_queue_t,因此,此处将把ngx_http_location_queue_t串联成双向链表,在图10-5中会看到这一点。

5)解析当前location{}配置块内的loc级别配置项。

图10-5为HTTP模块loc级别配置项结构体指针的内存示意图。

图10-5仍然是依据10.1节中的配置块例子所画的示意图,不过,这里仅涉及server块A(其server_name的参数值为A)以及它所属的location L1块。在解析http块时曾创建过1个ngx_http_core_loc_conf_t结构体(见10.2.1节),在解析server块A时曾经创建过1个ngx_http_core_loc_conf_t结构体(见10.2.2节),而解析其下的location块L1时也创建了ngx_http_core_loc_conf_t结构体,从图10-5中可以看出这3个结构体间的关系。下面先看看图10-5中ngx_http_core_loc_conf_t的3个关键成员:

10.2.3 管理location级别下的配置项 - 图2

图 10-5 HTTP模块loc级别配置项结构体指针的内存示意图


typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t;

struct ngx_http_core_loc_conf_s{

//location的名称,即nginx.conf中location后的表达式

ngx_str_t name;

/指向所属location块内ngx_http_conf_ctx_t结构体中的loc_conf指针数组,它保存着当前location块内所有HTTP模块create_loc_conf方法产生的结构体指针/

void**loc_conf;

/将同一个server块内多个表达location块的ngx_http_core_loc_conf_t结构体以双向链表方式组织起来,该locations指针将指向ngx_http_location_queue_t结构体/

ngx_queue_t*locations;

……

};


可以这么说,ngx_http_core_loc_conf_t拥有足够的信息来表达1个location块,它的loc_conf成员也可以引用到各HTTP模块在当前location块中的配置项。所以,一旦通过某种容器将ngx_http_core_loc_conf_t组织起来,也就是把location级别的配置项结构体管理起来了。但ngx_http_core_loc_conf_t又是放置在什么样的容器中呢?注意,图10-3在解析server块A时有1个ngx_http_core_loc_conf_t结构体,它的地位与server块A内的各个location块对应的ngx_http_core_loc_conf_t结构体是不同的,location L1、location L2块内的ngx_http_core_loc_conf_t是通过server A块内产生的ngx_http_core_loc_conf_t关联起来的。

在ngx_http_core_loc_conf_t结构体中有一个成员locations,它表示属于当前块的所有location块通过ngx_http_location_queue_t结构体构成的双向链表,如下所示。


typedef struct{

/queue将作为ngx_queue_t双向链表容器,从而将ngx_http_location_queue_t结构体连接起来/

ngx_queue_t queue;

/如果location中的字符串可以精确匹配(包括正则表达式),exact将指向对应的ngx_http_core_loc_conf_t结构体,否则值为NULL/

ngx_http_core_loc_conf_t*exact;

/如果location中的字符串无法精确匹配(包括了自定义的通配符),inclusive将指向对应的ngx_http_core_loc_conf_t结构体,否则值为NULL/

ngx_http_core_loc_conf_t*inclusive;

//指向location的名称

ngx_str_t*name;

……

}ngx_http_location_queue_t;


可以看到,ngx_http_location_queue_t中的queue成员将把所有相关的ngx_http_location_queue_t结构体串联起来。同时,ngx_http_location_queue_t将帮助用户把所有的location块与其所属的server块关联起来。

那么,哪些ngx_http_location_queue_t结构体会被串联起来呢?还是看10.1节的例子,server块A以及其下所属的location L1和location L2共包括3个ngx_http_core_loc_conf_t结构体,它们是相关的,下面看看它们是怎样关联起来的,如图10-6所示。

10.2.3 管理location级别下的配置项 - 图3

图 10-6 同一个server块下的ngx_http_core_loc_conf_t是通过双向链表关联起来的

每一个ngx_http_core_loc_conf_t都将对应着一个ngx_http_location_queue_t结构体。在server块A拥有的ngx_http_core_loc_conf_t结构体中,locations成员将指向它所属的ngx_http_location_queue_t结构体,这是1个双向链表的首部。当解析到location L1块时,会创建一个ngx_http_location_queue_t结构体并添加到locations双向链表的尾部,该ngx_http_location_queue_t结构体中的exact或者inclusive指针将会指向location L1所属的ngx_http_core_loc_conf_t结构体(在location后的表达式属于完全匹配时,exact指针有效,否则表达式将带有通配符,这时inclusive有效。exact优先级高于inclusive),这样就把location L1块对应的ngx_http_core_loc_conf_t结构体,以及其loc_conf成员指向的所有HTTP模块在location L1块内的配置项与server A块结合了起来。解析到location L2时会做相同处理,这也就得到了图10-6。

事实上,location之间是可以嵌套的,那么它们之间的关联关系又是怎样的呢?扩展一下10.1节中的例子,即假设配置文件如下:


http{

mytest_num 1;

server{

server_name A;

listen 8000;

listen 80;

mytest_num 2;

location/L1{

mytest_num 3;

……

location/L1/CL1{

}

}

}


这时多了一个新的location块L1/CL1,它隶属于location L1。此时,每个location块对应的ngx_http_core_loc_conf_t结构体间是通过如图10-7所示的形式组织起来的。

10.2.3 管理location级别下的配置项 - 图4

图 10-7 location块嵌套后ngx_http_core_loc_conf_t结构体间的关系

可以看到,仍然是通过ngx_http_core_loc_conf_t结构体中的locations指针来容纳属于它的location块的。当locations为空指针时,表示当前的location块下不再嵌套location块,否则表示还有新的location块。在10.2.4节的合并配置项的代码中可以看到这一点。