我们的一个web项目,由于新上城市增多,导致访问量增大,DB压力增大,作为提供接口的业务方,最近被下游反馈大量请求“502”。

502,bad gateway,一般都是upstream(这里就是php)出错,对于php,造成502的原因常见的就是脚本执行超过timeout设置时间,或者timeout设置过大,导致php进程长时间不能被释放,没有空闲worker进程来接客。

我们的项目就是php执行时间设置过短导致的,对于这种情况,可以先适当增大php的执行时间,先保证清除502,优化的事情毕竟要花更多的时间。

控制php执行时间的选项有两个,在php.ini中 max_execution_time 和php-fpm中 request_terminate_timeout,其中 request_terminate_timeout 可以覆盖 max_execution_time,所以如果不想改全局的php.ini,那只改php-fpm的配置就可以了。

下边我就来详细的分析一下为什么php脚本执行超出设置时间会导致nginx返回502。

先来布景,让问题复现:

nginx和php分别只启动一个worker,方便追踪。

php-fpm的request_terminate_timeout设置为3S。

测试脚本test.php

sleep(20);
echo 'ok';

go go go:

在浏览器访问www.v.com/test.php,3S后如期出现...404???what???

出师不利啊,赶紧看看nginx的配置文件

这个location配置是当发生5xx错误时跳转到一个好看点的界面,但是我在/usr/share/nginx/html下并没有50x.html这个文件。所以搞了个404出来。这不是很影响我判断问题的准确性?直接注释掉!再次访问,等待3S,终于'正常'的界面出来了。

环境好了,下边就上套路,按照web问题的排查套路走一遍,先看看错误日志吧:

nginx:

报错都是 recv() failed (104: Connection reset by peer。

recv时失败了,连接被重置了。为啥连接被重置了?难道一言不合。

我们在看看php-fpm的错误日志:

(注意php-fpm中php_admin_value[error_log]选项指定php的错误日志,会覆盖php.ini中的。但是这里不是看php的错误,而是看php-fpm的错误。php-fpm的错误日志由php-fpm.conf中的error_log选项指定。)

每一次请求都是产生2个WARNING和1个NOTICE:

WARNING:脚本执行超时了,终止了。

WARNING:子进程收到SIGTERM信号退出了。

NOTICE:启了一个新的子进程(因为我设置的pm.min_spare_servers = 1)

看来如果php的worker进程执行超时,不仅终止脚本执行,而且worker进程也会退出。看来nginx的报错连接被重置是因为php的worker进程退出了(在TCP连接中一方如果断掉的话会发送RST给另一方)

通过日志已经可以知道php脚本执行超时,worker子进程退出,导致nginx报错Connection reset by peer,下边我们通过strace来看看php和nginx的情况:

php:

1.accept一个nginx的连接请求(socket,bind,listen都在master中完成 ),可以看到nginx的端口是47039,从FD0中读取数据,就是从标准输入中,这个是fast-cgi协议规定的。accept之后的已连接描述符是3。

2.从FD3中读取nginx传递过来的数据,fastcgi协议格式,接收了856字节。为什么read5次呢?

因为fastcgi协议数据包是8字节对齐,由包头和包体组成。并且都是会先发一个request数据包,包含一些请求ID,版本,typpe等信息(包头包体各占8字节),再发一个params数据包,传递get参数和环境变量(包头8字节,包体变长),最后发送一个没有包体只有包头的params数据包,表示参数发送结束(包头8字节)。所以前3个read用来读出request包的包头和包体,还有params包的包头,第四个read是读取真正的数据,最后一个read是读取最后一个params包的包头。所以nginx传递的数据应该是8+8+8+856+8=896字节(和下边nginx的传输bytes能对应上)。注意如果是post方式,还会发送stdin数据包。

3.设置休眠20S,就是php程序中的sleep(20),之后由于进程被终止了,所以后边就没啦。strace程序也退出啦。

nginx:

1.accept到浏览器的请求,可以看到浏览器端的端口是56434,IP是192.168.1.105,已建立连接的FD是3。

2.从FD3中接收数据,HTTP协议。

3.创建一个socket,FD21,用于和php建立连接。

4.连接到FD21,可以看到连接的是本机的9000端口,这里nginx和php-fpm使用IP socket连接方式,nginx和php-fpm部署在一台机器上可以考虑unix domain socket。

5.向FD21写入数据,fast-cgi协议格式,我们看到写入的长度是896,和上边的php接收的长度是对应的。

6.recvfrom函数从FD21中返回 ECONNRESET (Connection reset by peer)

7.向FD9中写入错误信息,可以推断FD9就是nginx错误日志的文件描述符。

8.关闭和FD21的连接。

9.向FD3写入502 Bad Gateway,就是返回给浏览器的信息。

10.向FD8写入一条访问日志,可以推断FD8就是nginx访问日志的文件描述符。

来验证一下nginx访问日志和错误日志的推断。可以看到的确是FD8,FD9,并处于写入模式。

那么在这个过程中整个网络包的传输我们不妨也看一下:

通过tcpdump抓包,用神器看比较方便。

因为只想看nginx和php的通讯,在上边又知道nginx的端口是47039,可以通过tcp.srcport==47039过滤出对应的包。

可以看到nginx和php-fpm数据交互的过程:47039->9000建立三次握手,接着向9000发送数据,9000回复ACK,3S后9000回复RST。没毛病。

注意:

SYN,FIN各占一个序列号

ACK,RST不占序列号(28,29两个包的reqnum和acknum都是相同的)

序列号是每一字节加1(29包发送896字节,同时29包seq为4219146879,30包的ack为4219147775,正好相差896)

RST不需要回复。

深入分析HTTP状态码502(nginx+php-fpm)的更多相关文章

  1. Http状态码502问题复盘

    问题原因分析:502 bade gateway 一般都是upstream出错,对于PHP,造成502的原因常见的就是脚本执行超过timeout设置时间,或者timeout设置过大,导致PHP进程长时间 ...

  2. HTTP协议状态码详解

    HTTP状态码,我都是现查现用. 我以前记得几个常用的状态码,比如200,302,304,404, 503. 一般来说我也只需要了解这些常用的状态码就可以了.  如果是做AJAX,REST,网络爬虫, ...

  3. HTTP状态码面试必知

    typora-root-url: ./HTTPCODE HTTP状态码必知必会 这里主要介绍运维过程中经常遇到的状态码.并通过业界流行的Nginx进行模拟实现,让大家能有一种所见即所得的感觉.希望大家 ...

  4. Http状态码(转)

    什么是Http状态码?(转自http://bbs.tui18.com/thread-11597640-1-1.html) 百度百科上解释为:HTTP状态码(HTTP Status Code)是用以表示 ...

  5. HTTP状态码分类说明

    状态码分类 HTTP状态码被分为五大类, 目前我们使用的HTTP协议版本是1.1, 支持以下的状态码.随着协议的发展,HTTP规范中会定义更多的状态码. 小技巧:  假如你看到一个状态码518, 你并 ...

  6. HTTP协议 (六) 状态码详解

    HTTP协议 (六) 状态码详解 HTTP状态码,我都是现查现用. 我以前记得几个常用的状态码,比如200,302,304,404, 503. 一般来说我也只需要了解这些常用的状态码就可以了.  如果 ...

  7. Web开发中常用的状态码

    在HtttpServletResponse类中有关于状态码的描述. static int SC_ACCEPTED Status code (202) indicating that a request ...

  8. HTTP协议6之状态码--转

    HTTP状态码,我都是现查现用. 我以前记得几个常用的状态码,比如200,302,304,404, 503. 一般来说我也只需要了解这些常用的状态码就可以了.  如果是做AJAX,REST,网络爬虫, ...

  9. http协议状态码及其意义

    什么是状态码? 状态码的作用是:服务器告诉客户端,发生了什么事. 在http协议中状态码出现在http response 的第一行.它会返回一个三位数的状态码和状态信息.状态码为了便于程序进行处理,而 ...

随机推荐

  1. [Modern OpenGL系列(一)]十步搞定OpenGL开发环境

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51292143 OpenGL官网:https://www.opengl.or ...

  2. js动态绑定click事件时function传参问题

    今天碰到了这样一个问题,我在javascript中动态创建了一个button, 然后我想给改button添加click事件,绑定的function想要传入一个变量参数, 一开始我想直接通过函数传参传进 ...

  3. 使用gulp解决外部编辑器修改Eclipse文件延迟更新的问题

    本人前端用惯了Hbuilder,修改了eclipse项目中的文件后,由于是外部编辑器修改过的,eclipse不会自动部署更新,一般按F5刷新项目,或者在 preferences > genera ...

  4. 我的敏捷、需求分析、UML、软件设计电子书 - 下载(持续更新中)

    我将所有我的电子书汇总在一起,方便大家下载!(持续更新) 文档保存在我的网站——软件知识原创基地上(www.umlonline.org),请放心下载. 1)软件设计是怎样炼成的?(2014-4-1 发 ...

  5. id,clientid 和 uniqueid 的区别

    一. ID是设计的时候所指定的ID. ClientID是当这个控件生成到客户端页面时候,需要在客户端访问时候用的. UniqueID是当需要参与服务端回传的时候用的. 备注:当控件是子控件的时候(例如 ...

  6. ASP.NET MVC Routing、Areas、URLs

    webForm页面运行起来url一般是这样的:localhost:****/index.aspx,这个过程就是当你运行页面的时候,vs开发工具自带的微型服务器会打开你存在硬盘上的这个文件然后显示在浏览 ...

  7. js异步编程技巧一

    异步回调是js的一大特性,理解好用好这个特性可以写出很高质量的代码.分享一些实际用的一些异步编程技巧. 1.我们有些应用环境是需要等待两个http请求或IO操作返回后进行后续逻辑的处理.而这种情况使用 ...

  8. Unity性能优化(1)-官方教程The Profiler window翻译

    本文是Unity官方教程,性能优化系列的第一篇<The Profiler window>的简单翻译. 相关文章: Unity性能优化(1)-官方教程The Profiler window翻 ...

  9. 前端之float的几种清除浮动方式

    前端之float的几种清除浮动方式 本节内容 1.float清除方式1 2.float清除方式2 3.float清除方式3 4.float清除方式4 1.float清除方式1 <!DOCTYPE ...

  10. javascript中一些常见的兼容性问题

    下面是一些Javascript的IE和Firefox(火狐)兼容性的常用例子 1. document.formName.item("itemName") 问题 说明:IE下,可以使 ...