欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深入理解location匹配规则

程序员文章站 2022-05-22 21:14:04
...

在前面几篇文章中,为了表述某个问题,我们都会举例说明,其中用的最多的都是以locaiton开头的配置例子,形式基本如下:

 

location / {

    // 一些指令

}

 

特别是在ngx中的变量中出现的最多,它就是我们这篇文章要介绍的核心内容。

 


1
什么是location

       

它是nginx基于http协议跟外界沟通的桥梁,用来匹配并执行一个规范化的(normalized)URI,任何http请求都需要经过它的“同意”才能通过nginx的大门。那究竟什么样的请求可以进入这个大门呢?这取决于location 后面配置的是什么样的规则,比如我们常见的一种配置:

 

location /a {

   return 200 “hi $uri”;

}

 

表示以“/a”开头的请求都可以进入,当你通过curl以“/a”进行访问的时候,你会明确得出一个结果:

 

curl http://127.0.0.1/a

hi /a

 

那如果请求是“/a/b”和“/b/a”呢?很显然,第一个可以明确输出

 

hi /a/b

 

因为它符合以“/a”开头的规则。第二个因为不符合规则所以就直接返回404了(但如果b 是nginx可感知的文件路径,并且该路径下有一个a文件,那么请求“/b/a”会返回这个文件,这部分内容会在后续文章中提到,目前先按404对待)。

 

到这里我们会看到,这个location有点像战争题材电视剧中的哨兵,而这个规则就是口令,为了防止敌人潜入部队,巡逻的士兵或站岗的哨兵会和来访人员进行口令对比,如果来访人员答不上来,那麻烦可就大了,具体是哪种麻烦这取决于部队。

 

回到nginx,它的实际匹配方式并没有像我们之前介绍的那样单一,它可以有多种匹配模式,它们之间并没有优略之分,你可以根据实际场景来选择任何你认为合适的模式对请求进行匹配。

 


2
location的六种匹配模式

 

在nginx中,每个location块在匹配uri时都可以指定一个修饰符,不同的修饰符对请求的匹配范围是不一样的。 除了上面简单介绍的无修饰符location外,nginx中还有多种location修饰符,总结起来的话可以分为六种,分别是【无】【=】【^~】【~*】【~】【@】,而且每种修饰符都有它特定的匹配方式和特性,具体有哪些不同,我们会在接下来的内容中对其一一解释。

 

2.1精确匹配

在nginx中有一种匹配模式叫精确匹配(或严格匹配),它只需要在location后面加上 “=”这样的修饰符,像这样:

 

location = /a {

    return 200 “hi $uri”;

}

 

这种其实更像我们理解中的部队中口令,需要严格匹配。比如部队规定今天的口令是“瞌睡虫”,那么当哨兵喊口令的时候,对方必须说“瞌睡虫”,多一个字不行,少一个字也不行。所以,回到我们这个例子,只有请求是“/a”的时候才会输出唯一正确的值:

 

curl http://127.0.0.1/a

hi /a

 

当你多一个字符(如“/a/”),或少一个字符(如“/”)都不会给出正确的输出,这既是精确匹配。

 

2.2普通匹配

另外一种是我在工作中经常用的无修饰符的location和很少使用的“^~”修饰符的location,我们管它叫“普通匹配”。

 

关于【无】修饰符的locaiton我们在最开始已经提到过了,现在我们用带“^~”修饰的location来举个例子:

 

location ^~ /a {

    return 200 “hi $uri”;

}

 

然后用请求“/a”来访问一下:

 

curl http://127.0.0.1/a

hi /a

 

看起来和【=】、【无】这两种模式匹配无差啊,再换个请求方式,用“/ab”试试:

 

curl http://127.0.0.1/ab

hi /ab

 

到这里可以肯定它跟【=】匹配是不一样的,如果是精确匹配的话会直接返回404的。

 

我们知道,无修饰符的location是以其后设置的uri作为前缀来匹配请求的,而就目前来看【^~】这种匹配模式也是可以完成【无】模式工作的,所以感觉这种修饰符应该还会另有他用,会不会是后缀匹配呢?用“/b/a”和“/ca”试试看:

 

curl http://127.0.0.1/b/a

curl http://127.0.0.1/ca

 

结果这俩货不约而同的都打出了404,显然我们的推测是错误的。

 

实际上【无】和【^~】修饰符单在匹配uri规则上是完全一样的,都是以其后紧邻的uri作为前缀来匹配请求的。要说清楚这个问题需要把下一小节讲的内容“正则匹配”提前拿过来,因为它们的区别就在对正则匹配的处理上,来看下面这个例子(这两个location在同一个nginx配置中):

 

location /a {

return 200 “hi $uri”;

}

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

在上面的例子中,带“~”修饰符的location是可以正则匹配请求的,我们用请求“/a”来访问一下这个例子看会有什么效果:

 

curl http://127.0.0.1/a

Hi I am regex

 

通过结果可以看到,当前请求匹配的是支持正则匹配的那个locaiton,而且,似乎正则模式的匹配比无修饰符模式的匹配优先级要高。

 

那如果在无修饰符的location加上“^~”会是什么情况呢?来看实际例子(这两个location在同一个nginx配置中):

location ^~ /a {

return 200 “hi $uri”;

}

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

此时我们用同样的请求“/a”来发起请求:

 

curl http://127.0.0.1/a

hi /a

 

怎么样?是不是有点“拨乱反正”的感觉,当你加上“^~”修饰符后你的location一下子就提高了匹配优先级。

 

此时有些读者可能会想,是不是因为带“^~”修饰符的location放在了带“~”修饰符location前面的原因?真的是这样吗?那我们把它反过来再看看:

 

location ~ /a {

return 200 “Hi I am regex”;

}

 

location ^~ /a {

return 200 “hi $uri”;

}

 

用同样的uri发起请求:

 

curl http://127.0.0.1/a

hi /a

 

可以看到,跟location配置未颠倒之前的打印结果是一样的。

 

至此我们可以得出一个结论,【无】、【^~】这两种匹配模式在uri的匹配上是完全一致的,都是以其后配置的uri作为前缀来匹配请求的。不同的是当nginx配置文件中同时存在正则匹配时,【无】的匹配优先级要低于正则匹配,而【^~】的匹配优先级要高于正则匹配。

 

2.3正则匹配

正则匹配,顾名思义,就是支持正则的location,在nginx中可以使用【~】或【~*】来标识,一旦打上这个标识,那么在其后的uri就变成了正则表达式。其中,【~】表示匹配的过程要区分大小写,而【~*】则表示不区分大小写,其它方面两者没有任何区别,所以为了节省篇幅,本节仅用【~】来举例说明。

 

关于正则匹配的具体使用规则,我们还是老规矩,先看例子:

 

location ~ /a {

      return 200 “hello”;

}

 

然后直接用“/a”发起一个请求

 

curl http://127.0.0.1/a

hello

 

?,跟之前介绍的匹配没区别?别急,再用“/b/a”试试:

 

curl http://127.0.0.1/b/a

hello

 

这样看是不是就有区别了?像“/b/a”这样的请求,之所以可以匹配上“/a”,是因为加上了“~”符号的“/a”变成了一个正则表达式,此时任何带“/a”的字符串都可以被这个表达式匹配。

 

上面这个例子虽然用到了正则匹配,但是并没有用到正则的一些专有符号,所以总是感觉少点什么,现在我们再来举几个例子来把这一点给补上,先看一个简单的,用正则匹配模拟普通匹配

 

location ~  ^/a {

    return 200  “hello”;

}

 

当前配置中的“^”符号表示匹配字符串的“行首”,也可以简单理解成从行首开始匹配,所以“^/a”的意思就是,匹配以/a开头的字符串。这种规则是不是跟普通匹配模式很像?来,curl验证一下:

 

curl http://127.0.0.1/b/a  -v

< HTTP/1.1 404 Not Found

 

curl http://127.0.0.1/a/b  

hello

  

可以看到,之前可以正常返回结果的请求“/b/a”此时却返回了404,而请求“/a/b”则因为是以“/a”开头,所以返回了正确的结果。

 

简单的例子说完了,再来看一个稍微复杂一点的,假设现在有这样一个需求:我们需要某个location只匹配以“数字”开头,结尾是“.html”,且数字的个数最多不能多于5个,最少不能少于1个。对于这样的一个需求,可以用下面的正则匹配来完成:

 

location ~ “^/\d{1,5}\.html$” {

      return 200 “hello”;

}

 

其中“^”符号在前面已经说过了,用来表示行首。而“\d”符号在正则表达式中表示0到9的任意一个数字,紧接着用“{1,5}”来表示这个数字可以出现1到5次。随后是我们约定的以“.html”结尾,所以需要在最后加一个“行尾”符“$”。最后我们看到在“.html”前还有一个“\”符号,这其实是一个转义符,因为“.”在正则表达式中表示一个任意字符,前面加上转义符“\”后,“.”就只是一个普通的符号了。解释了那么多,用几个请求验证一下:

 

curl http://127.0.0.1/1234.html

hello

 

curl http://127.0.0.1/12t34.html

< HTTP/1.1 404 Not Found

 

curl http://127.0.0.1/.html

< HTTP/1.1 404 Not Found

 

从输出可以看到,除了第一个返回了正确的结果,其它的请求都因为匹配失败返回404。

 

在正则中,除了上面提到的“常规”匹配,还有一种叫子匹配(子模式、group等),nginx中对这种子匹配也是支持的,我们来看一个在nginx中使用子匹配的例子:

 

location ~  ^/(.)(.)(.) {

       return 200 “hello -> [$1]-[$2]-[$3]”;

}

 

其中“.”表示任意单个字符;“()”表示一个子匹配,而“$1”、“$2”、“$3”则表示对应的子匹配中的内容;在这个例子里有三个“()”符号,表示一共有三个子匹配,“$”符号后加数字可以表示对应的子匹配内容。用几个请求来看一下效果:

 

curl http://127.0.0.1/1234

hello -> [1]-[2]-[3]

 

curl http://127.0.0.1/abcd

hello -> [a]-[b]-[c]

  

通过输出结果并对照上面的解释,应该会很容易的明白子匹配在nginx中的使用,另外一点需要注意的是,在nginx中,最多支持9个子匹配,且是前9个,后续的会被忽略。

 

最后,本节主要讲述了正则表达式在nginx中的使用,关于正则表达式本身的更详细的规则,读者只能自己google了。

 

2.4内部匹配

除了上面提到的几种匹配模式,nginx中还有一种特殊的匹配,我们可以叫他“内部匹配”,在配置文件中使用【@】表示。这里“内部”的意思是指外部用户看不到的location,目前nginx中只有几个指令能看到这种匹配(比如error_page、try_files等)。

 

好,我们针对上面这句开场白来做几个例子,看看它究竟是如何玩的,首先看一个正常的配置:

 

location /a {

      return 200 “hello”;

}

   

这个例子已经看到无数次了,对于“/a”这样的请求一定会打印出“hello”。现在我们把这个例子改成内部匹配模式,像这样:

 

location @/a {

   return 200 “hello”;

}

  

注意这个“@”符号和后面的“/a”中间没有空格,否则的话nginx无法启动成功,不要问为什么,这是“龟腚”。好,现在再用“/a”请求一下试试:

 

curl http://127.0.0.1/a  -v

 

< HTTP/1.1 404 Not Found

< Content-Type: text/html

< Content-Length: 175

 

意料之中,直接404了。有的同学可能会说,是不是你的请求不对,应该用“@/a”请求才对,但是仔细一想,这种请求显然是不成立的,因为它的完整url会是这样:

 

http://127.0.0.1@/a 

   

是不是很奇怪,这根本就不是一个合法的请求。

 

外部正常请求的方式失败了,那所谓的内部匹配方式又是怎么玩的呢?本节开始的时候我们说过,目前nginx中只有几种指令可以使用这种匹配,这其中就包括我们常用的“error_page”指令。该指令在nginx的作用是为某些错误响应码指定一个uri,意思是当某个请求的响应发生错误时,它的响应码正好被“error_page”指定,那么它的响应内容将是该指令后对应的uri展示的内容。还是有点绕,直接看例子把:

 

error_page  404  /a;

location /a {

   return 200 “This is a error page”;

}

 

当前配置中只有一个“/a”location规则,为了演示这个例子,我们需要用这种location匹配不到的请求来访问,比如“/b”,根据以往的经验,如果没有“error_page”指令,这种请求应该会返回404,而加上这个指令后效果是这样的:

 

curl http://127.0.0.1/b -v

 

< HTTP/1.1 404 Not Found

< Content-Length: 9

 

This is a error page

 

可以看到,响应码还是404,但是响应内容已经变成“/a”location所输出的内容了。

 

但是现在有一个问题,对于“/a”这样一个配置,只有在其他请求出错的时候才会用到,所以我们显然是不想让用户直接访问的,这个时候【@】这种方式就排上用场了,这个配置可以改成这样:

 

error_page  404  @/a;

location @/a {

    return 200 “This is a error page”;

}

 

此时再用“/b”试一下

 

curl http://127.0.0.1/b -v

 

< HTTP/1.1 404 Not Found

< Content-Length: 9

 

This is a error page

 

看结果显然是符合预期的,而原来的“/a”在配置中已经不存在。

 

实际上对于【@】这种匹配方式,在nginx中是完全独立存在的,其它模式都是从NGX_HTTP_FIND_CONFIG_PHASE阶段(后续有文章专门介绍,这里知道有这么一个东西就行,不必深究)开始匹配location的,而【@】则超三界之外,不在五行之中,直接走自己独立的匹配道路。它的内部实现逻辑大致是这样:当nginx的所有location被加载完毕后,所有带“@”的location会被单独放到一个容器中,任何支持这种location的指令,在使用的时候都是去这个容器中做完全匹配的。而且这种匹配并没有uri的概念,也就是说,我们在使用的时候并不需要把它伪装成uri,像这样就可以:

 

error_page  404  @a;

location @a {

    return 200 “This is a error page”;

}

 

同时它也没有什么特殊的匹配规则(比如以某个字符串前缀来匹配),从字符匹配上来说,跟精确匹配很像,多一个字符不行,少一个字符也不行,必须严格匹配。

 

另外一种跟【@】这种内部匹配规则很像的是带有internal指令的location,不同的是【@】只能被极少数指令使用,而带internal指令的location则适用与所有的内部请求,它不会更改原location的匹配流程(比如【@】方式会绕过NGX_HTTP_FIND_CONFIG_PHASE阶段,直接从单独容器中匹配),只会更改当前location的访问权限。

 

那什么是内部请求?大部分内部请求都符合这么一个特点:外部请求进入nginx后,都或多或少被nginx“倒过手”。比如rewrite指令,对于一个有着internal的location,外部请求是不能直接访问的,但是被rewrite重写uri后就可以,比如下面这个例子:

 

location /a {

    internal;

    return 200 “hello”;

}

 

location /b {

     rewrite /   /a;

}

 

当我们用curl访问“/a”请求时会直接返回404,而用“/b”请求则可以打印出正确的结果“hello”,原因是因为rewrite指令更改了当前请求的内外性质,把它变成了一个内部请求。

 

内部请求的实现原理大致是这样:nginx在配置阶段会把包含internal指令的location都打上一个内部标识(比如internal=1),然后,当某个请求成功匹配到该location后,nginx会先检查当前请求是否也被打上了内部标识(比如r->internal=1),如果没有,则拒绝请求,反之则通过。而上面提到的rewrite就是这种可以为当前请求打上内部标识的指令,另外像error_page、try_files、index等指令都可以,还有像add_before_body这种用子请求的方式实现功能的指令也是可以的。(关于子请求,后续会有专门文章进行详细介绍)

 

2.5后缀是“/”的location

在nginx中还存在这样一种不太容易被发现的规则,当某个location的后缀是“/”时,该locaiton可以成功匹配一个后缀不是“/”符号的请求。

 

上面这句话读起来好像有点绕,没关系,来看一个实际的例子:

 

location /a/ {

      proxy_pass https://www.baidu.com/;

}

 

从前面学到的知识我们可以知道,上面这个location匹配的uri是以“/a/”开头的请求,而我们最上面那句话“该locaiton可以匹配上一个后缀不是“/”符号的请求”又扩大了这个location的匹配范围,意思是它也可以匹配“/a”这个请求,因为“/a”比配置中的location正好在结尾处少一个“/”。在验证这条规则之前我们先用正常的请求来访问一下看看结果:

 

curl http://127.0.0.1/a/ -v

  

< HTTP/1.1 200 OK

< Content-Type: text/html

< Content-Length: 2443

 

从响应头可以看出他返回了正确的内容,并且内容长度为2443个字节。好,接下来再用“/a”请求一下试试:

 

curl http://127.0.0.1/a -v

 

< HTTP/1.1 301 Moved Permanently

< Content-Type: text/html

< Content-Length: 190

< Location: http://127.0.0.1/a/

 

可以看到,并没有返回404,而是一个301,并且有一个Location响应头。仔细看这个响应头的内容,比我们原请求多了一个“/”符号。去掉scheme和

host部分,单看uri它正好是“/a/”跟我们例子中location配置的uri是一致的,有点类似于nginx自动修正了这个请求,把他导向了一个正确的匹配。

 

针对这条“/”后缀的规则,我们可能会产生另外一种疑问,如果没有“/”后缀的和有“/”这个后缀的location同时存在,那nginx会选择哪个来匹配?就近?随机?不猜了,举几个例子试一下:

 

location /a {

    proxy_pass https://www.jd.com/;

}

 

location /a/ {

    proxy_pass https://www.baidu.com/;

}

 

使用curl模拟“/a”请求,多请求几遍,最后出来的结果总是www.jd.com返回的结果,而用“/a/”请求最终出来的结果总是www.baidu.com返回的结果,即使我们把上面两个配置项上下颠倒,结果总是不变的。所以结论是只有类似“/a”这种配置不存在时,与之对应的“/a/”这种location配置在面对“/a”请求时才会表现出自动修正的动作。

 

另外,细心的读者可能会注意到,之前举例时locaiton里面都是“return”指令,而本节切用的“proxy_pass”指令,难道换个指令会有什么不同?还是用实际例子来看把:

 

location /a/ {

    return 200 “hello”;

}

 

同样的location,不一样的内部指令,先用“/a/”请求访问以下看看结果

 

curl http://127.0.0.1/a/

hello

   

看起来没啥问题,至少证明配置本身没有问题。再用“/a”试试,根据上面的经验,此时应该返回一个301响应码,并且有一个Location头,且内容是“http://127.0.0.1/a/”,究竟是不是呢?来看结果

 

curl http://127.0.0.1/a -v

 

< HTTP/1.1 404 Not Found

< Content-Type: text/html

< Content-Length: 175

 

意思很明显了,代表当前nginx没有一个可以匹配“/a”的location,所以,看到这个结果是不是很困惑?此时你可能会想,之前说的关于有“/”后缀的location规则到底还算不算数?

 

当然算数,只不过要加上一些限制了。在nginx中并不是所有的指令都支持location这种规则,目前在nginx-1.9.4中只有五个指令(最新版又加了一个grpc_pass),分别是proxy_pass,fastcgi_pass,uwsgi_pass,scgi_pass,memcached_pass。 实际上nginx中所有的location都有这种能力,只不过这种能力默认都是关闭的,如果有需要你可以随时把它打开,不过目前不可以通过配置文件的方式打开,只能通过硬编码的方式,而上面这五个指令之所有能支持这种能力,就是因为它们知道这种硬编码规则,并将其打开了。

 

到这里关于“/”后缀的规则基本已经结束了,但是通过前面的讲述我们知道,location的匹配有六种规则,目前我们所有的例子用的都是【无】,那么再刨去一个内部匹配【@】,其它四中是否适用这种“/”后缀规则呢?老规矩,直接上例子:

 

location = /a/ {

    proxy_pass https://www.baidu.com/;

}

 

location ^~ /b/ {

    proxy_pass https://www.baidu.com/;

}

 

location ~* /c/ {

    proxy_pass https://www.baidu.com;

}

 

location ~ /d/ {

    proxy_pass https://www.baidu.com;

}

 

 然后我们分别用“/a”,“/b”,“/c”,“/d”来发起请求,看看会有什么结果:

 

curl http://127.0.0.1/a -v

< HTTP/1.1 301 Moved Permanently

< Location: http://127.0.0.1/a/

 

curl http://127.0.0.1/b -v

< HTTP/1.1 301 Moved Permanently

< Location: http://127.0.0.1/a/

 

curl http://127.0.0.1/c -v

< HTTP/1.1 404 Not Found

  

curl http://127.0.0.1/d -v

< HTTP/1.1 404 Not Found

 

结果一目了然,【~】、【~*】这两种正则模式不支持这种“/”后缀规则,所以最后这种“/”后缀规则的适用范围还要刨除【~】、【~*】这两种模式匹配。

 


3
匹配模式的优先级

 

截止到目前,我们已经介绍完了所有六种匹配模式,如果每次只是使用其中的一种,那么对于看过上面内容的同学来说应该不是什么难事,但如果多种模式一起使用呢?比如某个请求正好处在多个模式的交集匹配范围内,此时应该以哪个模式优先呢?或者另外一个古怪的问题,这几个模式是否可以同时存在呢?

 

针对上面的问题,我们在nginx.conf文件中增加如下配置:

 

1:  location = /a {

      return 200 “[= /a]”;

}

 

2:  location /a {

       return 200 “[/a]”;

}

 

3:  location ^~ /a {

       return 200 “[^~ /a]”;

}

 

4:   location ~ /a {

        return 200 “[~ /a]”;

}

 

5:   location ~* /a {

       return 200 “[~* /a]”;

}

 

为了便于解释,每个location前都加了一个数字编号,另外因为【@】匹配模式是一个内部匹配,所以直接排除掉了他,这样一个配置文件中就有五中匹配模式了。

 

好了,有了上面的这个配置,第一步自然是先启动或是加载nginx,如果你是第一次启动nginx,那么你需要先启动它,我本机安装目录是/my/nginx,所以当进入到安装目录后,我需要做如下操作:

 

$ ./sbin/nginx

 

然后敲回车就可以。如果nginx已经是启动状态,那么直接reload就可以,操作如下:

 

$ ./sbin/nginx -s reload

 

同样回车就可以了。不管是哪种命令,没有消息就是最好的消息,代表启动或reload成功。因为我本机nginx已经启动,所以我直接reload就可以了,来看一下结果:

 

$ ./sbin/nginx -s reload

nginx:[emerg] duplicate location "/a" in /my/nginx/conf/nginx.conf:34

 

遗憾的是居然加载失败(reload)失败,提示信息说有重复的location,因为我们所有的location的uri部分都是“/a”,所以初步怀疑是nginx中不能同时存在同样的location。但是再仔细看这条提示信息,它打印出了错的行数为34,而在我的nginx.conf配置文件中,第34行正好是我们为其编号的第3号location,它的匹配模式是【^~】,所以如果真的是nginx中不能同时存在同样的location,那么是不是在第2号的时候就应该出错呢?有点一头雾水,先把3号location改成下面这样:

 

3:  location ^~ /ab {

       return 200 “^~ /a”;

}

 

然后再reload一次试试:

 

$ ./sbin/nginx -s reload

 

回车后发现可以成功reload,难道【^~】模式无法跟其它模式共存吗?当然不是,这其实是因为【^~】模式跟【无】发生了冲突,因为它俩本来就是同一个东西,只不过匹配完成后,后续的动作不一样了。试想一下,在同一个nginx.conf中有两个同样的location,但是其内部指令且不同,那么当有请求过来的时候,nginx应该如何抉择呢?是按照其在配置文件中的顺序匹配?还是随机匹配呢?还是说在【^~】和【无】中定一个优先级呢?nginx给的答案是,不会有这种存在,在配置阶段就将其视为一种错误存在,这条规则同样适用于【=】匹配,但是并不适用于正则匹配。

    

既然这样,我们就先把3号location放一放,先来看看其它四个location的优先级。先用curl来试一试实际结果:

 

curl http://127.0.0.1/a

[= /a]

 

curl http://127.0.0.1/aa

[~ /a]

 

第一个请求打印出了“[= /a]”应该不会有什么疑问,同样的uri,精确匹配的优先级最高也很合乎常理。而二个请求直接绕过了2号location,并打印出了4号location的结果,这个结果跟我们在2.2节介绍普通匹配时下的结论是吻合的。

 

所以目前的结论大致是这样:

   1.【=】

   2.【^~】

   3.【~】【~*】

   4.【无】

这里面需要注意的是,对于同样的uri,【^~】和【无】只能存在一个,且跟【=】一样,不能重复。但是我们看到,两个正则匹配居然可以重复存在同样的uri,那它们的优先级又是怎样的呢?我们把例子中的4号和5号location的位置互换一下,然后再用同样的请求来试一下结果:

 

curl http://127.0.0.1/a

[= /a]

 

curl http://127.0.0.1/aa

[~* /a]

 

可以看到,第二个请求的输出结果跟之前不一样了,这次是打印出了5号location的结果,所以我们基本可以得出一个结论,正则匹配模式的优先级取决于其在配置文件中的先后顺序。

 

最后,对于这几种模式的优先级,nginx又是如何实现的呢?

 

实际上实现方式也很简单,当nginx最终启动成功后,会有三个容器来存放不同的location,其中:

  • 【=】【无】【^~】在同一个容器(用S表示)中,并且在容器中按字符升序排 序,如果顺序一样则短的在前,比如uri“/abcd”,一定排在uri“/abc”后面。

  • 【~】【~*】两个正则模式在同一个容器(用R表示)中,不过他们并没有按字符升序或长短来排序,同配置文件中的顺序一致。

  • 【@】单独在一个容器中。

除了【@】,当有请求过来的时候,大致规则如下:

 

 深入理解location匹配规则
            
    
    博客分类: NginxC  

 

  1. nginx先从S容器中按顺序找匹配,如果匹配到一个【=】则终止后续匹配,否则继续。

  2. 如果在S容器中匹配到多个【^~】,则以最后一个为准,并返回匹配成功。

  3. 如果在S容器中匹配到多个【无】,则以最后一个为准,此时会记下这个匹配(记做L1),然后继续从R容器中找匹配。

  4. 如果在R容器中没有找到合适的正则匹配,则终止后续匹配并以L1作为最终匹配。

  5. 如果在R容器中找到一个合适的正则匹配,则终止后续匹配,并以当前匹配作为最终匹配。

  • 深入理解location匹配规则
            
    
    博客分类: NginxC  
  • 大小: 230.9 KB