乐趣区

关于nginx:记一次生产环境大面积404问题

写在后面

公布到线上的接口服务始终好端端的,明天忽然经营反馈说很多性能无奈失常应用。通过排查,发现前端调用后端接口时,局部接口呈现 404 的景象。明天,我到公司比拟晚,必定是哪个小伙伴昨晚上班,走出办公室前没有祷告服务器不要出问题。要把这个人揪出来,吊在服务器上——祭天!

文章已收录到:

https://github.com/sunshinelyz/technology-binghe

https://gitee.com/binghe001/technology-binghe

问题复现

得悉经营的反馈后,我迅速登录服务器排查问题。首先,查看了接口服务的启动过程失常。验证接口服务的 ip 和端口是否失常,后果也是没啥问题。接下来,通过 Nginx 转发申请,此时呈现了问题,无法访问接口。同时 Nginx 的 access.log 文件中输入了如下日志信息。

192.168.175.120 - - [26/Feb/2021:21:34:21 +0800] "GET /third/system/base/thirdapp/get_detail HTTP/1.1" 404 0 "http://192.168.175.100/api/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"
192.168.175.120 - - [26/Feb/2021:21:34:22 +0800] "GET /third/system/base/thirdapp/get_detail HTTP/1.1" 404 0 "http://192.168.175.100/api/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"
192.168.175.120 - - [26/Feb/2021:21:34:26 +0800] "GET /third/system/base/thirdapp/get_detail HTTP/1.1" 404 0 "http://192.168.175.100/api/index.html" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"

此时,从 Nginx 日志中发现,输入的状态为 404,未找到后端的接口服务。为了进一步定位问题,我间接在线上环境通过 curl 命令的形式来拜访接口服务,后果是失常的。

通过这一系列的操作之后,咱们就能够确定问题是出在 Nginx 上了。

问题剖析

Nginx 开启 debug 模块

既然曾经定位到问题了,那咱们接下来就要剖析下产生问题的具体起因了。既然是 Nginx 的问题,我第一工夫想到的就是调试 Nginx 查找谬误起因。于是我在服务器命令行输出了如下命令来查看装置 Nginx 时的配置状况。

nginx -V

留神:这里曾经为 Nginx 配置了零碎环境变量,如果没有配置零碎环境变量,则须要输出 nginx 命令所在目录的残缺门路,例如:

/usr/local/nginx/sbin/nginx -v

命令行输入了如下信息。

configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --add-module=/usr/local/src/fastdfs/fastdfs-nginx-module-1.22/src --with-openssl=/usr/local/src/openssl-1.0.2s --with-pcre=/usr/local/src/pcre-8.43 --with-zlib=/usr/local/src/zlib-1.2.11 --with-http_ssl_module

能够看到,装置 Nginx 时没有配置 Nginx 的 debug 模块。

于是我在服务器上找到了 Nginx 的安装文件,在命令行输出如下命令从新编译 Nginx。

cd /usr/local/src/nginx/  #进入 Nginx 的安装文件根目录
make clean                #革除编译信息
./configuration --prefix=/usr/local/nginx-1.17.8 --with-http_stub_status_module --add-module=/usr/local/src/fastdfs/fastdfs-nginx-module-1.22/src --with-openssl=/usr/local/src/openssl-1.0.2s --with-pcre=/usr/local/src/pcre-8.43 --with-zlib=/usr/local/src/zlib-1.2.11 --with-http_ssl_module --with-debug  #设置编译 Nginx 的配置信息
make     #编译 Nginx, 切记不要输出 make install

上述命令中,切记不要输出make install 进行装置。

执行完 make 命令后,会在当前目录的 objs 目录下生成 nginx 命令,此时咱们须要先进行 Nginx 服务,备份 /usr/local/nginx/sbin/ 目录下的 nginx 命令,而后将 objs 目录下的 nginx 命令复制到 /usr/local/nginx/sbin/ 目录下,而后启动 Nginx 服务。

nginx_service.sh stop   #通过脚本进行 Nginx 服务
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak #备份原有 nginx 命令
cp ./objs/nginx /usr/local/nginx/sbin/nginx #复制 nginx 命令
nginx_service.sh start #通过脚本启动 Nginx 服务

留神:这里,在进行 Nginx 服务前,曾经将此 Nginx 从接入层网关中移除了,所以不会影响线上环境。为了防止应用新编译的 nginx 命令重启 Nginx 呈现问题,这里通过脚本先进行 Nginx 服务,而后复制 nginx 命令后,再启动 Nginx 服务。

配置 Nginx 输入 debug 日志

在 Nginx 的 nginx.conf 文件中配置如下信息。

error_log  logs/error.log debug;

此时,开启了 Nginx 的 debug 日志性能,并将 debug 信息输入到 error.log 文件中。

剖析问题

接下来,在服务器命令行输出如下命令监听 error.log 文件的输入日志。

tail -F /usr/local/nginx/logs/error.log

而后模仿拜访 http 接口,能够看到 error.log 文件中输入如下信息。

2021/02/26 21:34:26 [debug] 31486#0: *56 http request line: "GET /third/system/base/thirdapp/get_detail HTTP/1.1"
2021/02/26 21:34:26 [debug] 31486#0: *56 http uri: "/third/system/base/thirdapp/get_detail"
2021/02/26 21:34:26 [debug] 31486#0: *56 http args: ""2021/02/26 21:34:26 [debug] 31486#0: *56 http exten:""
2021/02/26 21:34:26 [debug] 31486#0: *56 posix_memalign: 0000000000FF6450:4096 @16
2021/02/26 21:34:26 [debug] 31486#0: *56 http process request header line
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Host: 10.31.5.66"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:85.0) Gecko/20100101 Firefox/85.0"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Accept: */*"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Accept-Encoding: gzip, deflate"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Referer: http://192.168.175.100/api/index.html"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header: "Connection: keep-alive"
2021/02/26 21:34:26 [debug] 31486#0: *56 http header done
2021/02/26 21:34:26 [debug] 31486#0: *56 rewrite phase: 0
2021/02/26 21:34:26 [debug] 31486#0: *56 test location: "/"
2021/02/26 21:34:26 [debug] 31486#0: *56 test location: "file/"
2021/02/26 21:34:26 [debug] 31486#0: *56 test location: ~ "/base"
2021/02/26 21:34:26 [debug] 31486#0: *56 using configuration "/base"

从下面的输入日志中,咱们能够看到:拜访的接口地址为“/third/system/base/thirdapp/get_detail”,如下所示。

2021/02/26 21:34:26 [debug] 31486#0: *56 http uri: "/third/system/base/thirdapp/get_detail"

Nginx 在进行转发时,别离匹配了“/”,“file/”,“~/base”,最终将申请转发到了“/base”,如下所示。

2021/02/26 21:34:26 [debug] 31486#0: *56 test location: "/"
2021/02/26 21:34:26 [debug] 31486#0: *56 test location: "file/"
2021/02/26 21:34:26 [debug] 31486#0: *56 test location: ~ "/base"
2021/02/26 21:34:26 [debug] 31486#0: *56 using configuration "/base"

咱们再来看看 Nginx 的配置,关上 nginx.conf 文件,找到上面的配置。

location ~/base {
  proxy_pass                  http://base;
  proxy_set_header Host $host:$server_port;
}
location ~/third {
  proxy_pass                  http://third;
  proxy_set_header Host $host:$server_port;
}

那么问题来了,拜访的接口明明是“/third/system/base/thirdapp/get_detail”,为啥会走到“/base”上面呢?

说到这里,置信仔细的小伙伴曾经发现问题了,没错,又是运维的锅!!

解决问题

看了 Nginx 的配置后,置信很多小伙伴应该都晓得如何解决问题了,没错那就是把 nginx.conf 中的如下配置。

location ~/base {
  proxy_pass                  http://base;
  proxy_set_header Host $host:$server_port;
}
location ~/third {
  proxy_pass                  http://third;
  proxy_set_header Host $host:$server_port;
}

批改为如下所示。

location /base {
  proxy_pass                  http://base;
  proxy_set_header Host $host:$server_port;
}
location /third {
  proxy_pass                  http://third;
  proxy_set_header Host $host:$server_port;
}

去掉“~”符号即可。

接下来,再次模仿拜访 http 接口,可能失常拜访接口。

接下来,将 Nginx 的 debug 性能敞开,也就是将 nginx.conf 文件中的 error_log logs/error.log debug; 配置正文掉,如下所示。

# error_log  logs/error.log debug;

从新加载 nginx.conf 文件。

nginx_service.sh reload

最终,将 Nginx 退出到接入层网关,问题解决。

科普 Nginx 的转发规定

Nginx 的 location 语法

location [=|~|~*|^~] /uri/ {…}
  • = 严格匹配。如果申请匹配这个 location,那么将进行搜寻并立刻解决此申请
  • ~ 辨别大小写匹配(可用正则表达式)
  • ~* 不辨别大小写匹配(可用正则表达式)
  • !~ 辨别大小写不匹配
  • !~* 不辨别大小写不匹配
  • ^~ 如果把这个前缀用于一个惯例字符串, 那么通知 nginx 如果门路匹配那么不测试正则表达式

示例 1:

location  / {}

匹配任意申请

示例 2:

location ~* .(gif|jpg|jpeg)$ {
    rewrite .(gif|jpg|jpeg)$ /logo.png;
}

不辨别大小写匹配任何以 gif、jpg、jpeg 结尾的申请,并将该申请重定向到 /logo.png 申请

示例 3:

location ~ ^.+\.txt$ {root /usr/local/nginx/html/;}

辨别大小写匹配以.txt 结尾的申请,并设置此 location 的门路是 /usr/local/nginx/html/。也就是以.txt 结尾的申请将拜访 /usr/local/nginx/html/ 门路下的 txt 文件

alias 与 root 的区别

  • root 理论拜访文件门路会拼接 URL 中的门路
  • alias 理论拜访文件门路不会拼接 URL 中的门路

示例如下:

location ^~ /binghe/ {alias /usr/local/nginx/html/binghetic/;}
  • 申请:http://test.com/binghe/binghe…
  • 理论拜访:/usr/local/nginx/html/binghetic/binghe1.html 文件
location ^~ /binghe/ {root /usr/local/nginx/html/;}
  • 申请:http://test.com/binghe/binghe…
  • 理论拜访:/usr/local/nginx/html/binghe/binghe1.html 文件

last 和 break 关键字的区别

(1)last 和 break 当呈现在 location 之外时,两者的作用是统一的没有任何差别

(2)last 和 break 当呈现在 location 外部时:

  • last 应用了 last 指令,rewrite 后会跳出 location 作用域,从新开始再走一次方才的行为
  • break 应用了 break 指令,rewrite 后不会跳出 location 作用域,其整个生命周期都在以后 location 中。

permanent 和 redirect 关键字的区别

  • rewrite … permanent 永久性重定向,申请日志中的状态码为 301
  • rewrite … redirect 长期重定向,申请日志中的状态码为 302

综合实例

将合乎某个正则表达式的 URL 重定向到一个固定页面

比方:咱们须要将合乎“/test/(\d+)/[\w-.]+”这个正则表达式的 URL 重定向到一个固定的页面。合乎这个正则表达式的页面可能是:http://test.com/test/12345/ab…、http://test.com/test/456/1111…

从下面的介绍能够看出,这里能够应用 rewrite 重定向或者 alias 关键字来达到咱们的目标。因而,这里能够这样做:

(1)应用 rewrite 关键字

location ~ ^.+\.txt$ {root /usr/local/nginx/html/;}
location ~* ^/test/(\d+)/[\w-\.]+$ {rewrite ^/test/(\d+)/[\w-\.]+$ /testpage.txt last;
}

这里将所有符合条件的 URL(PS:不辨别大小写)都重定向到 /testpage.txt 申请,也就是 /usr/local/nginx/html/testpage.txt 文件

(2)应用 alias 关键字

location ~* ^/test/(\d+)/[\w-\.]+$ {alias /usr/local/nginx/html/binghetic/binghe1.html;}

这里将所有符合条件的 URL(不辨别大小写)都重定向到 /usr/local/nginx/html/binghetic/binghe1.html 文件

好了,明天就到这儿吧,我是冰河,大家有啥问题能够在下方留言,也能够加我微信:sun_shine_lyz,我拉你进群,一起交换技术,一起进阶,一起牛逼~~

退出移动版