未加星标

nginx 502 和 504 超时演示

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2018 | 作者 红领巾 ] 0人收藏点击收藏

最近线上 nginx 遇到了一些较难排查的 502 和 504 错误,顺便了解了一下 nginx 的相关配置。我发现网上很多介绍 nginx 超时配置只是列了这几个配置的含义和数值,并没有解释什么原因会触发哪个配置。因此趁这个机会演示一下,如何让 nginx 符合预期正确出现 502 和 504。

502 和 504 的解释

在 http status 的 定义 中:

502 Bad Gateway: The server was acting as a gateway or proxy and received an invalid response from the upstream server. 504: he server was acting as a gateway or proxy and did not receive a timely response from the upstream server.

502 的错误原因是 Bad Gateway,一般是由于上游服务的故障引起的;而 504 则是 nginx 访问上游服务超时,二者完全是两个意思。但在某些情况下,上游服务的超时(触发 tcp reset)也可能引发 502,我们会在之后详述。

演示环境

你需要 3 个逻辑组件:nginx 服务器php-fpm,client 访问客户端。3 个组件可以在同一台机器中,我用的是 docker 来配置 PHP 和 nginx 环境,在宿主机上访问。如果你很熟悉这 3 个组件,这部分可以跳过。用 docker 来做各种测试和实验非常方便,这里就不展开了。docker-compose 的配置参考了这篇 文章 。我的 docker composer 文件如下:

version: '3' services: web: image: nginx:alpine ports: - "8080:80" volumes: - ./code:/code - ./nginx/site.conf:/etc/nginx/conf.d/site.conf depends_on: - php php: image: php:7.1-fpm-alpine volumes: - ./code:/code - ./php/php-fpm.conf:/usr/local/etc/php-fpm.conf

使用的镜像都是基于 alpine 制作的,非常小巧:

REPOSITORY TAG SIZE php 7.1-fpm-alpin 69.5MB nginx alpine 18.6MB

nginx 的配置:

server { index index.php index.html; server_name php-docker.local; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /code; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_connect_timeout 5s; fastcgi_read_timeout 8s; fastcgi_send_timeout 10s; } }

php-fpm 的配置

[global] include=etc/php-fpm.d/*.conf request_terminate_timeout=3s

代码放在 github 。

关键参数

在这个演示中,PHP 的关键参数有两个,一个是 PHP 脚本的 max_execution_time,这个配置在 php.ini 中;另一个是 php-fpm 的 request_terminate_timeout,在 php-fpm.conf 中。当以 php-fpm 提供服务时,request_terminate_timeout 设置会覆盖 max_execution_time 的设置,因此我们这里只测试 request_terminate_timeout。

request_terminate_timeout 的意思是 php-fpm 接受的请求的超时时间,超过这个时间 php-fpm 会 kill 掉执行脚本的 worker 进程。

nginx的关键参数是 fastcgi 相关的 timeout,即:fastcgi_connect_timeout,fastcgi_read_timeout,fastcgi_send_timeout。

这几个 nginx 参数的主语都是 nginx,所以 fastcgi_connect_timeout 的意思是 nginx 连接到 fastcgi 的超时时间,fastcgi_read_timeout 是 nginx 读取 fastcgi 的内容的超时时间,fastcgi_send_timeout 是 nginx 发送内容到 fastcgi 的超时时间。

演示过程

首先启动 nginx 和 PHP:

docker-compose up

在 code 文件夹下添加一个 index.php 文件:

<?php sleep(70); echo 'hello world'; 上游服务主动 reset

访问 php-docker.local:8080/index.php,报错 502 bad gateway。而且是在 3s 之后报的错,说明触发了 request_terminate_timeout 设置,php-fpm 关闭了连接。

通过观察 ps aux | grep php 可以发现,php-fpm 是通过杀掉超时的进程来解决进程超时问题的(pid 每次有一个会变化,说明一个进程杀掉了,并启动了另一个进程。这和 php-fpm 的进程池设定有关,你的设定未必会重新启动一个新的进程)。

/var/www/html # ps aux | grep php 1 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 6 www-data 0:00 php-fpm: pool www 7 www-data 0:00 php-fpm: pool www /var/www/html # ps aux | grep php 1 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 7 www-data 0:00 php-fpm: pool www 17 www-data 0:00 php-fpm: pool www /var/www/html # ps aux | grep php 1 root 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf) 17 www-data 0:00 php-fpm: pool www 20 www-data 0:00 php-fpm: pool www

在这种情况下,nginx 日志中的错误是:

recv() failed (104: Connection reset by peer) while reading response header from upstream

即连接被服务端(PHP)reset 了,也就很好理解了。

注意,在这种情况下,php-fpm 的日志中也会记录的:

php_1 | [18-Jul-2018 16:33:42] WARNING: [pool www] child 5, script '/code/index.php' (request: "GET /index.php") execution timed out (3.040130 sec), terminating php_1 | [18-Jul-2018 16:33:42] WARNING: [pool www] child 5 exited on signal 15 (SIGTERM) after 30.035736 seconds from start php_1 | [18-Jul-2018 16:33:42] NOTICE: [pool www] child 8 started

这也是可以发现问题的一个地方。

nginx 读取上游服务超时

删掉 request_terminate_timeout 配置,重启应用:

docker-compose down && docker-compose up

此时,PHP 脚本将要执行 70s,肯定超过 nginx 设置的超时时间,get 一下发现确实如此,8s 之后抛出 504 Gateway Time-out 错误,nginx 日志是:

upstream timed out (110: Operation timed out) while reading response header from upstream

说明触发了 fastcgi_read_timeout 设置。

关闭上游服务

关掉 PHP 服务:

docker-composer stop php

PHP 服务停掉之后第一次访问,得到 504 错误,错误是:

upstream timed out (110: Operation timed out) while connecting to upstream

超时时间为 fastcgi_connect_timeout 的设置。说明这个时候 tcp 连接还在,但是尝试连接的时候失败了。

再次访问,得到 502 错误,错误是:

connect() failed (113: Host is unreachable) while connecting to upstream

502 的原因很容易理解,上游服务挂了,同时因为之前访问的时候发现连接不上就把连接断掉了,再次连接的时候便无法找到 host 了。

我曾怀疑第一次访问 504 是由于 keepalive。但我停掉 PHP 之后隔了好久才发第一个请求,仍然是这个结果。

如果将 nginx fastcgi_pass 配置为 127.0.0.1:9000(本地没有这个端口),则马上就会抛出 502 错误,错误为:

connect() failed (111: Connection refused) while connecting to upstream

登入 nginx 服务,使用 tcpdump 监控 9000 上的通信:

tcpdump -i eth0 -nnA tcp port 9000 # 如果你的 PHP 在本地,eth0 应该改成 lo

我们发现,当 PHP 关闭之后第一次访问,nginx 会尝试向 PHP 发起若干次 TCP SYN 请求,但 PHP 显然不会响应,这个时候 nginx 就返回了 504。第二次访问的时候 nginx 根本不会发起任何请求,直接 502 了。如果我们这个时候执行 nginx -t 会发现,nginx 已经认为配置文件有问题了:nginx: configuration file /etc/nginx/nginx.conf test failed。

换一种配置

这篇文章 提到,我们之前的 nginx 配置并不合理,我们重新设置 nginx:

server { index index.php index.html; server_name php-docker.local; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; root /code; resolver 127.0.0.11; # here location ~ \.php$ { set $upstream php:9000; # here try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass $upstream; # here fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_connect_timeout 5s; fastcgi_read_timeout 8s; fastcgi_send_timeout 10s; } }

其中 127.0.0.11 是 docker 的内网 dns resolver。该配置动态指定 fastcgi pass,所以 nginx 不会检查该连接能否建立起来。

按照这个配置启动,先访问 index.php 建立连接,然后关闭 PHP,表现为:

在 keepalive 期间,抛出 504 错误,超时时间为 fastcgi_connect_timeout,错误是:

upstream timed out (110: Operation timed out) while connecting to upstream

keepalive 断线之后,抛出 502 错误,超时时间不定,错误是:

connect() failed (113: Host is unreachable) while connecting to upstream 按照

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

主题: PHP服务器TIRY
tags: php,nginx,fastcgi,fpm,timeout,index,upstream,PHP,超时,docker,log,conf
分页:12
转载请注明
本文标题:nginx 502 和 504 超时演示
本站链接:https://www.codesec.net/view/580647.html


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(42)