zoukankan      html  css  js  c++  java
  • Nginx location 配置

    location匹配顺序

    1. "="前缀指令匹配,如果匹配成功,则停止其他匹配
    2. 普通字符串指令匹配,顺序是从长到短,匹配成功的location如果使用^~,则停止其他匹配(正则匹配)
    3. 正则表达式指令匹配,按照配置文件里的顺序,成功就停止其他匹配
    4. 如果第三步中有匹配成功,则使用该结果,否则使用第二步结果

    注意点

    1. 匹配的顺序是先匹配普通字符串,然后再匹配正则表达式。另外普通字符串匹配顺序是根据配置中字符长度从长到短,也就是说使用普通字符串配置的location顺序是无关紧要的,反正最后nginx会根据配置的长短来进行匹配,但是需要注意的是正则表达式按照配置文件里的顺序测试。找到第一个比配的正则表达式将停止搜索。

    2. 一般情况下,匹配成功了普通字符串location后还会进行正则表达式location匹配。有两种方法改变这种行为,其一就是使用“=”前缀,这时执行的是严格匹配,并且匹配成功后立即停止其他匹配,同时处理这个请求;另外一种就是使用“^~”前缀,如果把这个前缀用于一个常规字符串那么告诉nginx 如果路径匹配那么不测试正则表达式。

    匹配模式及顺序

      location = /uri    =开头表示精确匹配,只有完全匹配上才能生效。

      location ^~ /uri   ^~ 开头对URL路径进行前缀匹配,并且在正则之前。

      location ~ pattern  ~开头表示区分大小写的正则匹配。

      location ~* pattern  ~*开头表示不区分大小写的正则匹配。

      location /uri     不带任何修饰符,也表示前缀匹配,但是在正则匹配之后。

      location /      通用匹配,任何未匹配到其它location的请求都会匹配到,相当于switch中的default。 

    是的,作为一个程序员,你往往发现,有的时候你花费了数小时,数天,甚至数星期来查找问题,但最终可能只花费了数秒,改动了数行,甚至几个字符就解决了问题。这次给大家分享一个困扰了我很久,我花了五个小时才查找出问题原因,最终只添加了一个字符解决了的问题。

    配置时,可能遇到的一个问题

    问题描述

    我们的业务系统比较复杂,但最终提供给用户的访问接口比较单一,都是使用 Nginx 来做一个代理转发,而这个代理转发,往往需要匹配很多种不同类型的 URL 转给不同的服务。这就使得我们的 Nginx 配置文件变得很复杂,粗略估计了下,我们有近20个 upstream,有近60个 location 匹配。这些配置按照模块分布在不同的文件中,虽然复杂,但是仍然在我们的努力下运行的良好。直到有一天,有位同事给我反映说偶尔有些 URL 会出现 404 的问题。一开始没太在意,因为他也说不准是哪一种 URL 才遇到这个问题。

    问题查找

    后来,慢慢的查找,找到了一些规律,一开始只知道是 tomcat 那边返回 404了,想到 Nginx 都代理给了 tomcat,一开始就怀疑是程序的问题,不会想到是 Nginx。

    我开始查找代码的问题,我在本地的开发环境,尝试了很久,我使用 8080 端口访问,不论如何都是正确的结果,可是生产环境就是不行。然后我就听信了某坑友同事的理论,重启解决 95% 的问题,重装解决 100%的问题,我尝试重启了 tomcat 和 Nginx,依然不行,然后是重装,你猜结果如何????? ——想啥呢?当然也是不行!

    后来就开始怀疑是生产环境和开发环境的差异,去服务器上访问 8080 端口,仍然是可以的。可是一经过 Nginx 代理,就不行。这个时候才开始怀疑是 Nginx 出了什么问题。

    Nginx 怎么会出问题呢,业务系统中 URL 模式 /helloworld/* ,这样的 URL 我们都是统一处理的。怎么会出现一些行,一些不行呢。问题表现为 A URL (/helloworld/nn/hello/world)没问题,而 B URL(/helloworld/ii/hello/world) 有问题。

    所以到目前为止,基本可以肯定是 Nginx 的 location 上出了一些问题。

    问题解决

    因篇幅有限,为了直面本次问题的核心,我不再贴出完整的 Nginx 配置,我简化此次问题的模型。请看如下 Nginx 配置,这是我们之前的会导致问题的错误配置模型。

    worker_processes  1;
    error_log  logs/error.log;
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        log_format  main  '$remote_addr - $request_time - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';
    
        access_log  logs/access.log main;
    
        sendfile        on;
        keepalive_timeout  65;
    
        gzip  on;
    
        server {
            listen       80;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
            location = /helloworld {
                    return 602;
            }
            location /helloworld {
                    return 603;
            }
            
            ## 生产环境中如下两个 location 在另外一个文件中,通过 include 包含进来
            location /ii {
                    return 604;
            }
            location ~ /ii/[^/]+/[^/]+ {
                    return 605;
            }
            ##
            
            location ~ ^/helloworld/(scripts|styles|images).* {
                    return 606;
            }
        }
    }
    

    注意,这里有几点需要说明一下,生产环境的 Nginx 服务器配置文件比这里要复杂很多,而且是按模块分布在不同的文件中的。这里简化模型后,使用 Http 响应状态码 60x 来区分到底被哪个 location 匹配到了。
    我针对当时的情况,做了大量尝试,最终的简化版本如下:

    上面这些尝试支持读者自行试验,Nginx 配置文件是完整可用的,我本地 Nginx 的版本是1.6.2

    问题就在这里:我这里是事后,把这些匹配 location 标记成了不同的响应码,才方便查找问题。当发现这个不符合预期后,我还是难以理解,为何我一个以 /helloworld 开头的 URL 会被匹配到 605 这个以 /ii 开头的 location 里面来。在当时的生产环境中,以 /ii 的配置统一放在另外一个文件中,这里是很难直观的察觉出来这个 /ii 跟访问的 URL 里面的 /ii 的关系。

    我不得不重新编译了 Nginx ,加上了调试参数,修改配置项,看调试日志了。

    这里不再讲如何给 Nginx 加调试的编译参数,可自行查看相关文档。修改配置项很简单,只需要在

    error_log  logs/error.log;
    

    后面加上 debug 就可以了。

    打出详细调试日志后,访问
    http://localhost/helloworld/ii/hello/world 
    我得到了这样的一段日志(省略掉了前后无用的日志,只保留有意义的一段):

    2015/02/02 15:38:48 [debug] 5801#0: *60 http request line: "GET /helloworld/ii/hello/world HTTP/1.1"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http uri: "/helloworld/ii/hello/world"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http args: ""
    2015/02/02 15:38:48 [debug] 5801#0: *60 http exten: ""
    2015/02/02 15:38:48 [debug] 5801#0: *60 http process request header line
    2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "User-Agent: curl/7.37.1"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "Host: localhost"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http header: "Accept: */*"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http header done
    2015/02/02 15:38:48 [debug] 5801#0: *60 event timer del: 4: 1422862788055
    2015/02/02 15:38:48 [debug] 5801#0: *60 rewrite phase: 0
    2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "/"
    2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "ii"
    2015/02/02 15:38:48 [debug] 5801#0: *60 test location: "helloworld"
    2015/02/02 15:38:48 [debug] 5801#0: *60 test location: ~ "/ii/[^/]+/[^/]+"
    2015/02/02 15:38:48 [debug] 5801#0: *60 using configuration "/ii/[^/]+/[^/]+"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http cl:-1 max:1048576
    2015/02/02 15:38:48 [debug] 5801#0: *60 rewrite phase: 2
    2015/02/02 15:38:48 [debug] 5801#0: *60 http finalize request: 605, "/helloworld/ii/hello/world?" a:1, c:1
    2015/02/02 15:38:48 [debug] 5801#0: *60 http special response: 605, "/helloworld/ii/hello/world?"
    2015/02/02 15:38:48 [debug] 5801#0: *60 http set discard body
    2015/02/02 15:38:48 [debug] 5801#0: *60 posix_memalign: 00007FC3BB816000:4096 @16
    2015/02/02 15:38:48 [debug] 5801#0: *60 HTTP/1.1 605
    Server: nginx/1.6.2
    Date: Mon, 02 Feb 2015 07:38:48 GMT
    Content-Length: 0
    Connection: keep-alive
    

    可以看到,Nginx 测试了几次 location 匹配,最终选择了
    "/ii/[^/]+/[^/]+" 
    这个作为最终的匹配项。到这里问题就完全展现出来了,我们本来的意思,是要以 /ii 开头,后面有两个或者更多的 / 分割的 URL 模型才匹配,但是这里的正则表达式匹配写的不够精准,导致了匹配错误。正则表达式没有限制必须从开头匹配,所以才会匹配到 /helloworld/ii/hello/world 这样的 URL 。

    解决办法就是在这个正则表达式前面加上 ^ 来强制 URL 必须以 /ii 开头才能匹配.

    /ii/[^/]+/[^/]+ 
    变成
    ^/ii/[^/]+/[^/]+ 
    至此,这个坑被填上了,消耗的是五个小时和一个字符。

    相信很多人在写 Nginx 的location 的时候都会 location ~ /xxx 或者 location /iii 这样简单了事,但是我想说的是能尽量精确就尽量精确,否则出现问题的时候,非常难以查找。

    有关 Nginx 的 location 匹配规则,可以查看: http://nginx.org/en/docs/http/ngx_http_core_module.html

    参考

    https://blog.coding.net/blog/tips-in-configuring-Nginx-location

    https://www.cnblogs.com/coder-yoyo/p/6346595.html

  • 相关阅读:
    css 面试学习
    关于CSS的图像放大问题的解决,需要借助jQuery等直接用css3设置
    DEBUG使用
    crontab
    od
    dumpe2fs
    mke2fs
    dd
    GDB
    rm
  • 原文地址:https://www.cnblogs.com/oskyhg/p/9327699.html
Copyright © 2011-2022 走看看