Nginx 与 PHP FPM - 资源暂时不可用 - 502 错误
我正在使用一些代码发送超过 160 个 GET 请求 异步使用 curl到我的 API,该 API 在 Ubuntu 服务器 16.04 上运行带有 Php-fpm 的 Nginx。每个请求都会从数据库中提取不同的数据选择,然后将其作为 JSON 响应返回。这个请求数足够小,我认为它不应该达到各种默认限制(套接字连接数、文件描述符等)。然而,它们同时发送/接收的事实似乎导致了问题。
绝大多数请求都会成功,但有几个(在连续的测试中始终是相同的数量,但根据配置而有所不同)会得到“502 Bad Gateway”响应。
如果我查看 nginx 错误日志(/var/log/nginx/error.log
),我会看到以下错误消息:
2017/11/21 09:46:43 [error] 29#29: *144 connect() to unix:/run/php/php7.0-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream, client: 192.168.3.7, server: , request: "GET /1.0/xxx HTTP/1.1", upstream: "fastcgi://unix:/run/php/php7.0-fpm.sock:", host: "my.domain.org"
总是有确切地日志中的“502 Bad Gateway”错误消息的数量与我从 API 收到的数量相同。
同时,在执行测试期间查看 fpm 日志文件时(使用tail -100f /var/log/php7.0-fpm.log
),什么也没有发生。它只有以下内容:
[21-Nov-2017 11:54:29] NOTICE: fpm is running, pid 329
[21-Nov-2017 11:54:29] NOTICE: ready to handle connections
[21-Nov-2017 11:54:29] NOTICE: systemd monitor interval set to 10000ms
虽然我的 fpm 配置(在/etc/php/7.0/fpm/php-fpm.conf
)用 指定了错误日志error_log = /var/log/php7.0-fpm.log
,但似乎没有这样的文件,表明没有错误。
工作配置
我发现,如果我调整 fpm 配置,并且我将文件配置/etc/php/7.0/fpm/pool.d/www.conf
为使用static
多个15
线程而不是动态生成进程或使用较少数量的静态进程,我就可以让 Web 服务器正常工作(没有 502 错误)。
pm = static
pm.max_children = 15
我相信这是可行的,因为已经有足够的线程准备好应对突然的打击,并且不会因生成或关闭线程而产生延迟。但是,这确实意味着我的网络服务器将使用比我希望的更多的内存。理想情况下,我希望该pm.max_children
数字等于服务器上 vCPU 数量的 2 倍(即 8 个或更少)。在这种情况下,我使用的是四核服务器,但希望可以扩展向下到双核实例。理想情况下,我希望服务器能够回答全部即使总时间很长,也要及时处理请求,例如排队和调整超时。
配置设置
默认的 php-fpmlisten.backlog
值是511
,但我将其设置为 2000,只是为了消除它的影响。
listen.backlog = 2000
对于 Nginx,我设置了 1024 worker_connections
,worker_processes auto;
所以应该是 4。
我还有以下缓冲区和超时设置,以尝试防止它们成为一个因素:
##
# Buffere settings
##
client_body_buffer_size 10M;
client_header_buffer_size 1k;
client_max_body_size 512m;
large_client_header_buffers 2 1k;
##
# Timeout settings
##
client_body_timeout 120;
client_header_timeout 120;
keepalive_timeout 120;
send_timeout 120;
fastcgi_connect_timeout 60s;
fastcgi_next_upstream_timeout 40s;
fastcgi_next_upstream_tries 10;
fastcgi_read_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_cache_lock_timeout 60s;
值得注意的是,我们在大约 20 秒内收到了所有请求(包括 502),因此我们并没有达到这些要求。此外,即使fastcgi_next_upstream_tries
设置为 10,对于每个 502 错误消息,我只会收到 1 条资源不可用消息,而不是 10 次尝试应该尝试的 10 倍。
类似/相关问题
我发现 serverfault 和 stack overflow 上有很多类似的问题。我在这里详细介绍这些问题,这样这个问题就不会被标记为重复。
Serverfault - 使用 Nginx 和 PHP-FPM 时,请求永远不会在 pm.max_children 之后排队。似乎是一个非常相似的问题,但尽管它是 3 年前发布的,但还没有答案,而且它的细节远不如这里。另外,一些我的请求必须成功排队,而不像问题所说的那样,一旦达到最大值,所有请求都会被丢弃。
ServerFault - nginx 错误 502 & 资源暂时不可用)连接上游时,客户端。这篇文章看起来很相似(他描述了同样的问题),但正如其中一个答案指出的那样,他的套接字文件不匹配,而我的是匹配的。我的
/etc/php/7.0/fpm/pool.d/www.conf
配置文件有:监听 = /run/php/php7.0-fpm.sock
您可以看到它与 nginx 提供的错误消息中的套接字文件一致。
ServerFault - 需要增加 nginx 到上游 unix 套接字的吞吐量——Linux 内核调整?这里的答案建议设置
net.core.somaxconn
,net.core.netdev_max_backlog
我相应地将其设置为 4096 和 1000。问题仍然存在。ServerFault-连接到上游时 php-fpm.sock 失败(11:资源暂时不可用)- 这里的建议是让 pm = ondemand 并将 max_children 设置为 4000。这对我来说不适合,因为它可能导致我的四核服务器有 4000 个线程并耗尽内存。
问题
我相信Nginx 速度太快,PHP-fpm 端无法处理。有时 fpm 无法响应 nginx 请求,因此 Nginx 放弃并返回 502 错误。有没有办法(可能是一两个配置变量)来修复此问题,以便 fpm 将请求排队,或者让 nginx 稍后重试(fastcgi_next_upstream_tries
似乎没有任何效果)?我不介意长的它需要 Web 服务器来处理所有请求(增加超时),只需将我的 fpm 进程数设置为相对于我的 CPU 的适当数字,所有这 160 个请求都将得到处理。
更新-使用 TCP 套接字工作正常
我刚刚尝试将 FPM 从监听 unix 文件套接字切换到 TCP 套接字,详情如下这里。
例如将 fpm 更改为:Listen 127.0.0.1:9000
并更新 nginx 以使用:
fastcgi_pass 127.0.0.1:9000;
这似乎已经解决了问题。例如,即使我使用动态池或仅具有 2 个 fpm 线程的静态池,也不会收到任何 502 错误。
但是,我很想知道为什么这样做会有效,而不是使用本地 unix 文件套接字,以及是否只需进行配置更改即可使基于文件套接字的解决方案起作用,因为这是默认设置,并且许多人可能会使用它。
答案1
我相信你可以使用ngx_http_limit_req_module为了实现这一点,将数字配置为所需的 r/s 并使用 burst 设置队列大小,配置类似于:
limit_req_zone $binary_remote_addr zone=php:10m rate=2r/s;
server {
location ~ \.php$ {
limit_req zone=php burst=10;
}
此示例将平均每秒允许 2 个请求,并将第三到第十个请求(如果有)排队。如果 r/sa 超过 10,503
将返回错误(limit_req_status
)
答案2
转到您的 php-fpm 配置并添加 listen.backlog = 5000 注意硬限制是 65536
另外,确保它也受系统级检查支持,例如 sysctl net.core.somaxconn 如果它小于 5000,则执行 echo "net.core.somaxconn=10000" >> /etc/sysctl.conf sysctl -p
更多信息请点击这里: https://easyengine.io/tutorials/php/fpm-sysctl-tweaking/