您的位置:首页 > 运维架构 > Nginx

[nginx源码分析]location划分

2015-05-07 14:33 274 查看
整个ngx_http_block中的ngx_conf_parse配置解析完成,,后面主要对配置文件进行优化。
优化可以分为以下:
1 配置作用域合并
2 location划分
3 http header回调初始化hash
4 初始化http收包回调函数
5 server中的server_name形成hash

先对location类型概述:
Syntax:	location [ = | ~ | ~* | ^~ ] uri { ...}

location = / {
#matches the query /only
[ configuration A ]
}

location / {
#match any query ,since all queries begin with /, but regular
#expression and any longer conventional blocks will be
#match first
[ configuration B ]
}

location ^~ /images/ {
#matches any query beginning with /image/ 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 be /images/ directory with be handled by config c
[ configuration D ]
}

从location语法中可以看到,基本可以分为4类locaiton
1 只包含一个url的是前缀匹配,即以这个指定字符为前缀的请求地址都能与它匹配上,比如请求地址”/document.html”, 以字符串”/”为前缀,所以能与配置B匹配上(当然,最终的匹配结果是否就是配置B,还需要看其他location配置,因为nginx采用的是最佳匹配)
2 如果加上=表示绝对匹配,则表示绝对匹配location,在上面的示例,只有当前处理请求的uri完全匹配字符串”/”(即不能多一个字符,也不能少一个字符)时,才被定位并使用对应的相关配置A
3 正则匹配location是由限定符” ~ ”(区分大小写)或“~*“(不区分大小写)指定的,此时给出的uri是一个正则表达式,请求地址满足该正则表达式的就能匹配上。
4 由限定符”^~”指定的location也是前缀匹配location,不过它暗示了在实际进行location定位时不用搜索正则匹配locaiton
5 还有另外两种地址分别称之为命名location和未命名locaiton,命名locaiton仅用于server内部跳转,看如下中rewrite就是命名location。
location@rewrite{
         rewrite^/wiki/search(.*)$/search.php?search=$1 last;
}
 
其中在配置解析的时候(往前翻), 已经解析过location是保存在server上下文的loc_conf的locations 链表中。
下面的分析师对链表中的几点进行修剪。
函数是ngx_http_init_locations
首先是对server下的location进行排序,调用的函数是ngx_queue_sort,因为locations本身就是一个双向链表,然后该函数是典型的插入排序,比较函数是ngx_http_cmp_locations
ngx_queue_sort(locations,ngx_http_cmp_locations)
比较函数ngx_http_cmp_locations的算法原则是:
1 首先是如果比较的额两个节点中插入的是未命名的,那么把该节点加入到后面,如果比较的两个节点都是未命名的,那么保持原定次序。
2 如果插入的两个节点中,插入的是命名的location,那么把该节点加入到后面,如果比较的两个节点都是命名的,那么比较location名称,按照字母序进行排序。
3 如果两个比较节点中,插入的是正则location,那么就把插入即诶的那加入到后面,如果比较的两个节点都是正则,那么就按照原定次序,即保持用户在配置文件里书序的先后顺序。
所以插入的降序是未命名、命名、正则、前缀匹配|绝对匹配。
static ngx_int_t
ngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two)
{
ngx_int_t                   rc;
ngx_http_core_loc_conf_t   *first, *second;
ngx_http_location_queue_t  *lq1, *lq2;

lq1 = (ngx_http_location_queue_t *) one;
lq2 = (ngx_http_location_queue_t *) two;

first = lq1->exact ? lq1->exact : lq1->inclusive;
second = lq2->exact ? lq2->exact : lq2->inclusive;

if (first->noname && !second->noname) {
/* shift no named locations to the end */
return 1;
}

if (!first->noname && second->noname) {
/* shift no named locations to the end */
return -1;
}

if (first->noname || second->noname) {
/* do not sort no named locations */
return 0;
}

if (first->named && !second->named) {
/* shift named locations to the end */
return 1;
}

if (!first->named && second->named) {
/* shift named locations to the end */
return -1;
}

if (first->named && second->named) {
return ngx_strcmp(first->name.data, second->name.data);
}

#if (NGX_PCRE)

if (first->regex && !second->regex) {
/* shift the regex matches to the end */
return 1;
}

if (!first->regex && second->regex) {
/* shift the regex matches to the end */
return -1;
}

if (first->regex || second->regex) {
/* do not sort the regex matches */
return 0;
}

#endif

rc = ngx_strcmp(first->name.data, second->name.data);

if (rc == 0 && !first->exact_match && second->exact_match) {
/* an exact match must be before the same inclusive one */
return 1;
}

return rc;
}

排序完后进行location切分。location排序完,整个list的结构是:
前缀匹配|绝对匹配--->正则匹配--->命名--> 未命名
切分就是遍历整个locationslist,然后找到正则匹配开始,命名匹配开始处
//这层循环主要是遍历整个locations链表找到regex的起始位置和named的起始位置
for (q = ngx_queue_head(locations);
q != ngx_queue_sentinel(locations);                        //遍历每一个location
q = ngx_queue_next(q))
{
lq = (ngx_http_location_queue_t *) q;

clcf = lq->exact ? lq->exact : lq->inclusive;

if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {    //这里是一个递归,如果存在location下面还有locations的话还会进行递归调用
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;
}
}

其他切分就不细说了,代码很easy,然后location就剩下前缀匹配和绝对匹配了。
 
然后对前缀匹配进行优化,函数是ngx_http_init_static_location_trees
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  nginx