忙完了今天的工做, 终于到了周五,能够好好休息下了。php
就在安心养神的时候, 同事转给了我一条nginx 502的报警, 赶忙去线上一顿排查。nginx
首先得先找出哪台机器报出的(同时喊运维看下线上负载状况), 发现01机器的nginx日志在报警时间点的错误信息:数据库
*272881176 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: xx.xx.xx.xx, server: , request: "POST /xxx/xxx HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "xx.xx.xx.xx:8081"centos
recv()为接收返回数据的系统函数,基本能够先认为报错缘由为bash
Nginx发现某服务与本身通讯的链接断掉了,就会返回给客户端502错误。服务器
那么nginx是从哪里接收数据呢,报错信息一样很明显,fastcgi://127.0.0.1:9000markdown
一样思考为何php的处理进程会中断呢?运维
一样针对这两种状况,排查结果:socket
顺便也看了下 fpm的错误日志、慢日志,也没有什么收获(此处极可能会忽略掉了重要信息)tcp
因而既然认为是fpm出了问题,就调研下fpm的配置文件吧
pid = /usr/local/var/run/php-fpm.pid #pid设置,必定要开启,上面是Mac平台的。默认在php安装目录中的var/run/php-fpm.pid。好比centos的在: /usr/local/php/var/run/php-fpm.pid error_log = /usr/local/var/log/php-fpm.log #错误日志,上面是Mac平台的,默认在php安装目录中的var/log/php-fpm.log,好比centos的在: /usr/local/php/var/log/php-fpm.log log_level = notice #错误级别. 上面的php-fpm.log纪录的登记。可用级别为: alert(必须当即处理), error(错误状况), warning(警告状况), notice(通常重要信息), debug(调试信息). 默认: notice. emergency_restart_threshold = 60 emergency_restart_interval = 60s #表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数若是超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项通常保持默认值。0 表示 '关闭该功能'. 默认值: 0 (关闭). process_control_timeout = 0 #设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0. daemonize = yes #后台执行fpm,默认值为yes,若是为了调试能够改成no。在FPM中,可使用不一样的设置来运行多个进程池。 这些设置能够针对每一个进程池单独设置。 listen = 127.0.0.1:9000 #fpm监听端口,即nginx中php处理的地址,通常默认值便可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每一个进程池都须要设置。若是nginx和php在不一样的机器上,分布式处理,就设置ip这里就能够了。 listen.backlog = -1 #backlog数,设置 listen 的半链接队列长度,-1表示无限制,由操做系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41 listen.allowed_clients = 127.0.0.1 #容许访问FastCGI进程的IP白名单,设置any为不限制IP,若是要设置其余主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每一个地址是用逗号分隔. 若是没有设置或者为空,则容许任何服务器请求链接。 listen.owner = www listen.group = www listen.mode = 0666 #unix socket设置选项,若是使用tcp方式访问,这里注释便可。 user = www group = www #启动进程的用户和用户组,FPM 进程运行的Unix用户, 必需要设置。用户组,若是没有设置,则默认用户的组被使用。 pm = dynamic #php-fpm进程启动模式,pm能够设置为static和dynamic和ondemand #若是选择static,则进程数就数固定的,由pm.max_children指定固定的子进程数。 #若是选择dynamic,则进程数是动态变化的,由如下参数决定: pm.max_children = 50 #子进程最大数 pm.start_servers = 2 #启动时的进程数,默认值为: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 pm.min_spare_servers = 1 #保证空闲进程数最小值,若是空闲进程小于此值,则建立新的子进程 pm.max_spare_servers = 3 #,保证空闲进程数最大值,若是空闲进程大于此值,此进行清理 pm.max_requests = 10000 #设置每一个子进程重生以前服务的请求数. 对于可能存在内存泄漏的第三方模块来讲是很是有用的. 若是设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0. pm.status_path = /status #FPM状态页面的网址. 若是没有设置, 则没法访问状态页面. 默认值: none. munin监控会使用到 ping.path = /ping #FPM监控页面的ping网址. 若是没有设置, 则没法访问ping页面. 该页面用于外部检测FPM是否存活而且能够响应请求. 请注意必须以斜线开头 (/)。 ping.response = pong #用于定义ping请求的返回相应. 返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong. access.log = log/$pool.access.log #每个请求的访问日志,默认是关闭的。 access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%" #设定访问日志的格式。 slowlog = log/$pool.log.slow #慢请求的记录日志,配合request_slowlog_timeout使用,默认关闭 request_slowlog_timeout = 10s #当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off' request_terminate_timeout = 0 #设置单个请求的超时停止时间. 该选项可能会对php.ini设置中的'max_execution_time'由于某些特殊缘由没有停止运行的脚本有用. 设置为 '0' 表示 'Off'.当常常出现502错误时能够尝试更改此选项。 rlimit_files = 1024 #设置文件打开描述符的rlimit限制. 默认值: 系统定义值默承认打开句柄是1024,可以使用 ulimit -n查看,ulimit -n 2048修改。 rlimit_core = 0 #设置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整数. 默认值: 系统定义值. chroot = #启动时的Chroot目录. 所定义的目录须要是绝对路径. 若是没有设置, 则chroot不被使用. chdir = #设置启动目录,启动时会自动Chdir到该目录. 所定义的目录须要是绝对路径. 默认值: 当前目录,或者/目录(chroot时) catch_workers_output = yes #重定向运行过程当中的stdout和stderr到主要的错误日志文件中. 若是没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: 空. 复制代码
单独拿出几个重要配置项:
pm = static #php-fpm进程启动模式,pm能够设置为static和dynamic和ondemand #若是选择static,则进程数就数固定的,由pm.max_children指定固定的子进程数。 pm.max_children = 500 #子进程最大数 request_terminate_timeout=30 #设置单个请求的超时停止时间. 该选项可能会对php.ini设置中的'max_execution_time'由于某些特殊缘由没有停止运行的脚本有用. 设置为 '0' 表示 'Off'.当常常出现502错误时能够尝试更改此选项。 request_slowlog_timeout=3 #当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off' pm.max_requests=10000 #设置每一个子进程重生以前服务的请求数. 对于可能存在内存泄漏的第三方模块来讲是很是有用的. 若是设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0. 复制代码
以上即是咱们线上的主要配置,主要仍是集中在了 request_terminate_timeout 这个参数上。它和php.ini的 max_execution_time 有什么区别
set_time_limit()函数和配置指令max_execution_time只影响脚本自己执行的时间。任何发生在诸如使用system()的系统调用,流操做,数据库操做等的脚本执行的最大时间不包括其中,而 request_terminate_timeout 是包含全部时间的
php.ini配置时间一样也为30,可是相比而言,request_terminate_timeout时间会更短。
但前文咱们明明说了,这个接口并非很复杂啊。..应该不会超时啊,当时第三方服务也没有什么异常状况,fpm错误日志也并无这个超时错误信息。各个依赖的系统负载都还处于比较低峰期状态
在疑问中结束了今天的工做,回来打算写一下分享今天的调试经历,在搜索文档的时候又发现了这么一句话:
在php.ini和php-fpm.conf中分别有这样两个配置项:max_execution_time和request_terminate_timeout。
这两项都是用来配置一个PHP脚本的最大执行时间的。当超过这个时间时,PHP-FPM不仅会终止脚本的执行,
还会终止执行脚本的Worker进程。因此Nginx会发现与本身通讯的链接断掉了,就会返回给客户端502错误。
以PHP-FPM的request_terminate_timeout=30秒时为例,报502 Bad Gateway错误的具体信息以下:
1)Nginx错误访问日志:
2013/09/19 01:09:00 [error] 27600#0: *78887 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 192.168.1.101, server: test.com, request: "POST /index.php HTTP/1.1", upstream: "fastcgi://unix:/dev/shm/php-fcgi.sock:", host: "test.com", referrer: "test.com/index.php"
2)PHP-FPM报错日志:
WARNING: child 25708 exited on signal 15 (SIGTERM) after 21008.883410 seconds from start
复制代码
因此只需将这两项的值调大一些就可让PHP脚本不会由于执行时间长而被终止了。
request_terminate_timeout能够覆盖max_execution_time,因此若是不想改全局的php.ini,那只改PHP-FPM的配置就能够了。
此外要注意的是Nginx的upstream模块中的max_fail和fail_timeout两项。有时Nginx与上游服务器(如Tomcat、FastCGI)的通讯只是偶然断掉了,但max_fail若是设置的比较小的话,那么在接下来的fail_timeout时间内,Nginx都会认为上游服务器挂掉了,都会返回502错误。因此能够将max_fail调大一些,将fail_timeout调小一些。
摘自最棒的51cto: blog.51cto.com/nanchunle/1…
对于nginx的upstream模块并非很了解,回忆当时报错场景,确实发现日志里面先后报了几个不一样接口一样的错误,也多是其余接口影响了此接口,只是它正好被报警系统抓取到。
思考问题可能过于片面,没有解决问题的一套体系、思路,容易绕弯路、甚至南辕北辙。
这是一篇没有结论的文章.
后续会尝试复现此场景,但愿你们持续关注。
欢迎关注 < 呆呆熊一点通 >