记 php-fpm 重启导致的 程序执行中断问题

背景和初步排查

  • 订单业务对账时报警了,有笔订单在我们自己的mongo库里没有找到
  • 业务接口 /3/xx/vgift/send 调用支付网关 sendPresent 接口完成送礼, 之后写mongo,但是php error log 里却查不到任何mongo异常日志
  • 写mongo没有异常,但是库里却没记录; 推断只有2个可能,1是error log 丢日志了。2是程序执行过程中操作完sendPresent后down掉了,导致没写入mongo
  • 第一个情况工作多年的经验来看应该不至于,那就先根据第二种情况继续查吧
  • 那就去看下php-fpm 的日志,看对应的时间点有没有什么异常
[wu.daolin@web-game001.m6~]$ grep "2017 05:28" /var/log/php-fpm.log
[25-Jun-2017 05:28:01] NOTICE: Terminating ...

  • 跟订单时间刚好吻合,那肯定有必要研究下了

熟悉下 php-fpm 的管理

php-fpm 是通过 php-fpm这个命令进行管理的,我们先看下这个命令

man php-fpm

这里有提到,php-fpm then responds to several POSIX signals php-fpm 会对下面几个信号作(自己的)处理

  • SIGINT, SIGTERM: immediate termination
  • SIGQUIT: graceful stop
  • SIGUSR1: re-open log file
  • SIGUSR2: graceful reload of all workers + reload of fpm conf/binary
动手验证下
  • sudo kill -QUIT {php-fpm-pid}
[26-Jun-2017 13:58:22] NOTICE: Finishing ...
[26-Jun-2017 13:58:22] NOTICE: exiting, bye-bye!

  • sudo kill -TERM {php-fpm-pid}
[26-Jun-2017 13:59:21] NOTICE: Terminating ...
[26-Jun-2017 13:59:21] NOTICE: exiting, bye-bye!
  • sudo kill -USR2 12583

[26-Jun-2017 14:00:48] NOTICE: Reloading in progress ...
[26-Jun-2017 14:00:48] NOTICE: reloading: execvp("/usr/sbin/php-fpm", {"/usr/sbin/php-fpm", "--daemonize"})
[26-Jun-2017 14:00:48] NOTICE: using inherited socket fd=8, "10.30.60.87:9000"
[26-Jun-2017 14:00:48] NOTICE: using inherited socket fd=8, "10.30.60.87:9000"
[26-Jun-2017 14:00:48] NOTICE: fpm is running, pid 12696
[26-Jun-2017 14:00:48] NOTICE: ready to handle connections

从验证结果推断

05:28:01这个时间有人给php-fpm 发送了SIGTERM信号,在这个点发生很可能是个定时任务, 确认果然是这样 28 5 * root /etc/init.d/php-fpm restart> /dev/null

我们的 php-fpm 管理
  • init script 是 /etc/init.d/php-fpm
  • 其中stop 是 killproc -p ${pidfile} php-fpm, 显然从日志结果来个是kill -TERM . 文档里也说了默认信号就是TERMkillproc sends signals to all processes that use the spec­ified executable. If no signal name is specified, the signal SIGTERM is sent.

看下这个情况下nginx的反应

总结原因

  • 业务请求时执行完 sendPresent这个动作后 , 还没来得及写mongo库, php-fpm就刚好被 terminate 了,.... 刚好赶上了

替代方案

  • 虽然php-fpm 没有解释 terminategraceful stop 的具体含义, 但猜的话前者是直接就终止程序的执行了,后者可能是温柔点,把处理中的请求里的所有操作都执行完再杀死。。。
  • 总之 SIGTERM terminate 调php 工作进程太粗暴了,应该要改一下比较好
  • 改成 SIGUSER2 reload 方式
  • 改成 SIGQUIT方式 ,把killproc -p ${pidfile} php-fpm 这句 改成 killproc -p ${pidfile} php-fpm -QUIT
  • php-fpm 的worker 是计数n次后就会杀掉重新拉一个,如果用reload感觉功能重复了,根本没必要定时重启了, 我还是选 graceful stop(SIGQUIT) 吧
  • 当然还有个问题时,为啥要配置个定时重启,将上面的内容发给sa看了

sa 的问答

sa 说了3点意见
  • 建议看下 -QUIT 时,Nginx的状态码是否正常?另外在某种情况下,可能会造成 PHP-FPM 进程退出时间比较长,会影响部署吗?
  • reload(SIGUSER2) 而不是用SIGTERM停掉再启动.
    我们之前的测试结果看 reload 之后,nginx会报 502,并不 graceful stop。建议做好测试确认,包括部署php代码时是不是 reload?
    Bug #60961 Graceful Restart (USR2) isn't very graceful
  • php-fpm每天定时重启脚本
    这个定时脚本大概是在2012年部署的,当时是担心 PHP-FPM 存在内存泄漏的情况而添加的。到现在是不是还适用?建议找一台机器关掉定时脚本观察一段较长时间看看。
我回复
  • SIGQUIT 是否正常还不清楚,但现在的默认 SIGTERM 是立即停掉php 进程是肯定不正常的 -- 从nginx error log 看,对于nginx 和 php-fpm已经建立好的连接,错误是 “104: Connection reset by peer”; 准备去连的是“111: Connection refused”;
  • “111: Connection refused” 是还可以接受的,连不上而已,用户稍后重试就可以;“104: Connection reset by peer” 这个就很难接受,这个错我理解的意思是连接已经建好了,php突然terminate了,然后发了个RST分节给nginx;背后就表示当前请求可能只执行了一半动作,还有动作没执行完,这可能就造成丢数据了。。。比如文章开头说的这个问题
  • reload 那个其实就是 -USR2信号,这个bug看起来还没解决。。。不过-USR2 应该说是偶现terminate,但 -TERM 肯定是必现terminate
  • 现在代码部署逻辑是同步代码+清理opcache和yac缓存, 不对php-fpm进程做操作
  • php-fpm 会自己对worker进程处理的请求数计数,达到一定数量就干掉再重新拉一个; 所以worker进程应该没有什么内存泄露的问题; manager 进程就不清楚了,但我想概率应该是极其低的。这个适不适用感觉很难去证伪啊。。。
  • 所以要不找3台机器, 一台用 -QUIT, 一台用 -USR2, 一台去掉这个定时任务;先观察下
  • sa 回复可以,我们自己看着办

尾声

  • 改成 SIGQUIT 信号nginx里还是有 104: Connection reset by peer, 看来手册里说SIGQUIT: graceful stop 也不能保证一次请求里的所有动作都执行完啊
  • 最终结果 去掉这个定时重启php-fpm 的任务, 已经3个多月了,没发现问题,oh yeah~

参考文档

时间: 2017-10-25

记 php-fpm 重启导致的 程序执行中断问题的相关文章

Windows服务器重启导致filebeat无法启动

早上6点钟, 收到zabbix的告警, 说一台服务器重启了, 回到公司马上查看系统日志,发现只有这些记录: 这不是坑爹么! 肯定是意外关闭啊, 但是为什么会是意外关闭呀? 这个问题后续需要再跟进, 现在暂不讨论, 因为有个更加急迫的故障需要处理: filebeat无法启动. 既然系统无法启动, 咱们去服务管理那边试下: 运行 - 输入services.msc 找到filebeat的服务后, 手动启动失败, 得到错误: 在谷歌上搜索一番之后, 找到一个解决办法: 我的电脑-->右键-->管理--

file-solaris下编译的程序执行是出现段错误

问题描述 solaris下编译的程序执行是出现段错误 编译器版本Reading specs from /usr/sfw/lib/gcc/sparc-sun-solaris2.10/3.4.3/specs Configured with: /sfw10/builds/build/sfw10-patch/usr/src/cmd/gcc/gcc-3.4.3/configure --prefix=/usr/sfw --with-as=/usr/ccs/bin/as --without-gnu-as --

对《30个提高Web程序执行效率的好经验》的理解

阅读了博客园发布的IT文章<30个提高Web程序执行效率的好经验>,这30条准则对我们web开发是非常有用的,不过大家可能对其中的一些准则是知其然而不知其所以然. 下面是我对这些准则的理解和分析,有些有关JS性能的准则,我也测试了它们的差异,大家可以下载DEMO页面,如有理解不正确的地方,请大家指正.也非常欢迎大家补充. 测试环境: OS:Vista; Processor:3.40GHz; Memory: 2.00GB; System type: 32-bit Operating System

c语言-一个求函数的积分的程序执行问题

问题描述 一个求函数的积分的程序执行问题 #define _CRT_SECURE_NO_DEPRECATE #include double integrate(double double int); double equa(double); /*函数说明,背积函数,具体函数可替换*/ float a b c; void main() { double leftlimit rightlimit;/*积分上限,下限*/int n;/*梯形划分数*/ printf(""请输入被积分函数的系数

PHP程序执行报错分析

程序|执行 一.执行PHP Zend加密文件报错现象:打开网站,出现以下错误Fatal error: Unable to read **** bytes in或者是Fatal error: Corrupted encoded data detected原因:这种是由于php页面采用了Zend加密,因此FTP上传方式要使用二进制方式.不能采用auto和ASCII,否则就会出现这个错误一定要确保下载和上传时都采用二进制方式,不然都有可能产生错误 二.访问出现500 Internal Server E

路由器重启导致无法网络连接的解决方法

在学校或某些使用交换机路由器组成局域网的场合,如果设备出现停电,往往会有些电脑无法联网.下面讲一下解决办法 步骤/方法 此类故障的停网一般不会出现全部电脑无法联网的情况,所以首先你要到可以联网的人的电脑上获得ip地址.首先进入控制面板 选择网络连接选项. 在本地连接上双击可查看选项卡. 选项卡的内容如下,在支持里面我们可以看到ip和网关.我们要 然后按照上面的方法进入你自己电脑的本地连接,选择查看属性 然后再属性弹出的对话框,选择ip选项 在接下来你要做的就是把你抄的ip和网关地址对应填入.至于

PowerShel程序执行完后删除脚本自身的方法

  这篇文章主要介绍了PowerShel程序执行完后删除脚本自身的方法,本文直接给出代码实例,需要的朋友可以参考下 当脚本执行完成后,可以删除自身 代码如下: #删除脚本自身 remove-item $MyInvocation.MyCommand.Path -force

任务栏的图标在程序执行的时候怎么隐藏掉?

问题描述 任务栏的图标在程序执行的时候怎么隐藏掉? 请问用什么api可以隐藏掉任务栏上正在执行的图标?图标怎么隐藏起来? 解决方案 一个是直接隐藏窗口,一个是通过taskvisible实行来隐藏. 解决方案二: 隐藏任务栏上的图标 解决方案三: 你的意思是不要在任务栏显示运行的图标?你是啥语言?如果是c# ,可以直接设置属相的.

如何用php获取程序执行的时间_php技巧

在head.htm中加入,也就是在默认模版中添加"$stime=microtime(true); //获取程序开始执行的时间" 复制代码 代码如下: <!--<?php$stime=microtime(true); //获取程序开始执行的时间$GuideFid[$fid]=str_replace("<a href='$webdb[www_url]' class='guide_menu'>>首页</a>","&quo