Nginx 返回 5xx 状态码常用解决办法

2020-08-04 10:03 Nginx 688

502 Bad Gateway

官方解释:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。

上面说到 nginx 收到了无法理解的响应,什么是无法理解的响应呢?以 php 和 nginx 为例子:

  • nginx 无法与 php-fpm 进行连接。
  • nginx 在连接 php-fpm 一段时间后发现与 php-fpm 的连接被断开。

那么什么时候会出现上面的情况呢?

  • php-fpm 没有启动, nginx 无法将请求交给 php-fpm
  • php-fpm 运行脚本超时,php-fpm 终止了脚本的执行和执行脚本的 Worker 进程,nginx 发现自己与 php-fpm 的连接断开

php-fpm 没有启动

我们关闭 php-fpm,刷新页面,发现返回 502 错误:
image

php-fpm 请求超时

我们首先将 php-fpm.conf 中的 max_terminate_request改成 5s:

request_terminate_timeout = 5

在 php 脚本中添加如下语句:

sleep(20);

刷新页面,发现返回 502 错误:
image

其他 502 原因的检测

1、查看当前的 PHP FastCGI 进程数是否够用

netstat -anpo | grep "php-cgi" | wc –l

如果实际使用的【FastCGI 进程数】接近预设的【FastCGI 进程数】,那么,说明【FastCGI 进程数】不够用,需要增大。
2、部分 PHP 程序的执行时间超过了 Nginx 的等待时间可以适当增加 nginx.conf 配置文件中 FastCGI 的 timeout 时间。php.ini 中 memory_limit 设低了会出错,修改了 php.ini 的 memory_limit 为 64M,重启 nginx,如果发现恢复了,那么就是 PHP 的内存不足的原因。
3、max-children 和 max-requests
主机上运行着 nginx php(fpm) xcache 的话,访问量日均 300W pv 左右。如果是近期出现 php 页面打开很慢,cpu 使用率突然降至很低,系统负载突然升至很高,查看网卡的流量,也会发现突然降到了很低这样的情况,而且这种情况只持续数秒钟就恢复,这时检查 php-fpm 的日志文件发现了一些线索:

1)Sep 30 08:32:23.289973 \[NOTICE\] fpm\_unix\_init\_main(), line 271: getrlimit(nofile): max:51200, cur:51200
2)Sep 30 08:32:23.290212 \[NOTICE\] fpm\_sockets\_init\_main(), line 371: using inherited socket fd=10, “127.0.0.1:90003)Sep 30 08:32:23.290342 \[NOTICE\] fpm\_event\_init\_main(), line 109: libevent: using epoll
4)Sep 30 08:32:23.296426 \[NOTICE\] fpm\_init(), line 47: fpm is running, pid 30587

看显示的这几句的前面,是 1000 多行的关闭 children 和开启 children 的日志。因为 php-fpm 有一个参数 max_requests,该参数指明每个 children 最多处理多少个请求后便会被关闭,默认的设置是 500。因为 php 是把请求轮询给每个 children,在大流量下,每个 children 到达 max_requests 所用的时间都差不多,这样就造成所有的 children 基本上在同一时间被关闭。
在这期间,nginx 无法将 php 文件转交给 php-fpm 处理,所以 cpu 会降至很低,不用处理 php,更不用执行 sql,而负载会升至很高,关闭和开启 children、nginx 等待 php-fpm,网卡流量也降至很低,nginx 无法生成数据传输给客户端。
解决方式:增加 children 的数量,并且将 max_requests 设置为 0 或者一个比较大的值,打开 /usr/local/php/etc/php-fpm.conf,调大max-children 和 max-requests两个参数,但是要根据主机实际情况,数值过大也不行。然后再重启 php-fpm,就能恢复了。
4、增加缓冲区容量大小
将 nginx 的 error log 打开,发现【pstream sent too big header while reading response header from upstream】这样的错误提示。大概意思是 nginx 缓冲区有一个 bug 造成的,网站的页面消耗占用缓冲区可能过大,增大client head buffer,fastcgi buffer size这两个参数可以解决。
5、PHP 脚本的最大执行时间

max_execution_time           //php.ini
request_terminate_timeout    //php.ini

这两项都是用来配置 PHP 脚本的最大执行时间。超时时 php-fpm 会终止脚本的执行,同时还会终止执行脚本的 Worker 进程。
如上,php-fpm child 18822 被 terminate 后重新生成了新的 Worker 进程 19164, 所以 nginx 发现与自己通信的连接断了,就自然会返回 502 错误给客户端。客户端需再次发起请求重新建立新的连接,表象是刷新下浏览器即重新发起请求
所以只需将这两项的值适当调大,让 PHP 脚本不会因为执行时间长而被终止从而与 nginx 激活连接丢失。
request_terminate_timeout 优先级高于 max_execution_time,不想改全局的 php.ini,只改 php-fpm 的配置就可以了。这里暂且调到 600 秒

request_terminate_timeout = 600

504 Bad Gateway timeout

504 即 nginx 超过了自己设置的超时时间,不等待 php-fpm 的返回结果,直接给客户端返回 504 错误。但是此时 php-fpm 依然还在处理请求(在没有超出自己的超时时间的情况下).
这里有三个相关的配置:

  • fastcgi_connect_timeout 300;

    指定连接到后端 FastCGI 的超时时间.

  • fastcgi_send_timeout 300;

    向 FastCGI 传送请求的超时时间,这个值是指已经完成两次握手后向 FastCGI 传送请求的超时时间.

  • fastcgi_read_timeout 300;

    接收 FastCGI 应答的超时时间,这个值是指已经完成两次握手后接收 FastCGI 应答的超时时间.
    网关超时,客户端所发出的请求没有到达网关, 在限定时间内没有得到 php-fpm,或者完成 php-fpm 的传输数据的工作而超时 。比方说:即 nginx 的 worker 去 php-fpm 进程池去处理,但是没有 fpm 进程可以使用了,等啊等,还是没有,返回 504。

场景
nginx 的 fastcgi 模块有一个 fastcgi_read_timeout 配置,它表示从 FastCGI server 获取数据的超时时间。如果超过这个配置客户端就是收到 504 的响应。比如执行了一段非常耗时的查询语句,nginx 的相关 fastcgi 等待配置超时后,就会返回 504,但是 php-fpm 还在运行。
解决办法

location ~ \.php$ {
    fastcgi_connect_timeout  180;//优化点
    fastcgi_read_timeout     600;//优化点
    fastcgi_send_timeout     600;//优化点
    fastcgi_pass unix:/tmp/php-fpm.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
}

调高上面标绿的 3 个值后,主要是 read 和 send 两项 (默认 Nginx 超时为 60),完美地解决了 504 错误。
并且可以配置在 http,server 级别,也可以配置在 location 级别。

factcgi_connect_{read|send|timeout}是对fastcgi_pass生效
proxy_connect_{read|send|timeout|是对proxy_pass生效
赞赏码 给点吧😭