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


Posted in PHP onApril 29, 2019

背景和初步排查

  • 订单业务对账时报警了,有笔订单在我们自己的mongo库里没有找到
  • 业务接口  /3/xx/vgift/send 调用礼物系统  sendPresent 接口完成送礼, 之后写mongo,但是php error log 里却查不到任何mongo异常日志
  • 写mongo没有异常,但是库里却没记录,推断只有2个可能

1是error log 丢日志了
2是程序执行过程中操作完sendPresent后down掉了,导致没写入mongo
-第一个情况工作多年的经验来看应该不至于,那就先根据第二种情况继续查吧

  • 那就去看下php-fpm 的日志,看对应的时间点有没有什么异常
[wu.daolin@web001.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的反应

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

总结原因

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

替代方案

  • 虽然php-fpm 没有解释 terminate 跟 graceful 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~

参考文档

  • php-fpm signal handler
  • php-fpm init script
  • killproc man page

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

PHP 相关文章推荐
透析PHP的配置文件php.ini
Oct 09 PHP
深入PHP购物车模块功能分析(函数讲解,附源码)
Jun 25 PHP
浅析PHP绘图技术
Jul 03 PHP
ueditor 1.2.6 使用方法说明
Jul 24 PHP
本地机apache配置基于域名的虚拟主机详解
Aug 10 PHP
PHP抓取、分析国内视频网站的视频信息工具类
Apr 02 PHP
PHP版微信第三方实现一键登录及获取用户信息的方法
Oct 14 PHP
win7 wamp 64位 php环境开启curl服务遇到的问题及解决方法
Sep 16 PHP
CentOS7.0下安装PHP5.6.30服务的教程详解
Sep 29 PHP
PHP的mysqli_ssl_set()函数讲解
Jan 23 PHP
laravel orm 关联条件查询代码
Oct 21 PHP
PHP类的自动加载与命名空间用法实例分析
Jun 05 PHP
详解提高使用Java反射的效率方法
Apr 29 #PHP
Thinkphp整合阿里云OSS图片上传实例代码
Apr 28 #PHP
详解php中生成标准uuid(guid)的方法
Apr 28 #PHP
PHP写API输出的时用echo的原因详解
Apr 28 #PHP
PHP+mysql防止SQL注入的方法小结
Apr 27 #PHP
PHP常用工具函数小结【移除XSS攻击、UTF8与GBK编码转换等】
Apr 27 #PHP
PHP操作路由器实现方法示例
Apr 27 #PHP
You might like
使用GD库生成带阴影文字的图片
2015/03/27 PHP
PHP爬虫之百万级别知乎用户数据爬取与分析
2016/01/22 PHP
php连接oracle数据库的方法(测试成功)
2016/05/26 PHP
浅谈PHP中静态方法和非静态方法的相互调用
2016/10/04 PHP
php框架CodeIgniter使用redis的方法分析
2018/04/13 PHP
javascript demo 基本技巧
2009/12/18 Javascript
JavaScript 滚轮事件使用说明
2010/03/07 Javascript
HTTP 304错误的详细讲解
2013/11/13 Javascript
js中typeof的用法汇总
2013/12/12 Javascript
JQuery $.each遍历JavaScript数组对象实例
2014/09/01 Javascript
jQuery控制Div拖拽效果完整实例分析
2015/04/15 Javascript
JavaScript学习小结(一)——JavaScript入门基础
2015/09/02 Javascript
js采用concat和sort将N个数组拼接起来的方法
2016/01/21 Javascript
js获取时间精确到秒(年月日)
2016/03/16 Javascript
Node.js利用Net模块实现多人命令行聊天室的方法
2016/12/23 Javascript
js实现拖拽功能
2017/03/01 Javascript
bootstrap手风琴折叠示例代码分享
2017/05/22 Javascript
详解使用angular的HttpClient搭配rxjs
2017/09/01 Javascript
vue+springboot实现项目的CORS跨域请求
2018/09/05 Javascript
小程序实现列表删除功能
2018/10/30 Javascript
微信小程序全局变量的设置、使用、修改过程解析
2019/09/24 Javascript
python解析xml文件实例分享
2013/12/04 Python
利用Django框架中select_related和prefetch_related函数对数据库查询优化
2015/04/01 Python
python3实现短网址和数字相互转换的方法
2015/04/28 Python
python中偏函数partial用法实例分析
2015/07/08 Python
vscode 远程调试python的方法
2017/12/01 Python
对Python3中列表乘以某一个数的示例详解
2019/07/20 Python
pycharm快捷键汇总
2020/02/14 Python
HTML5新增加标签和功能概述
2016/09/05 HTML / CSS
Black Halo官方网站:购买连衣裙、礼服和连体裤
2018/06/13 全球购物
创业计划书的内容步骤和要领
2014/01/04 职场文书
中学自我评价
2014/01/31 职场文书
关于母亲节的感言
2014/02/04 职场文书
广告业务员岗位职责
2014/02/06 职场文书
《雪儿》教学反思
2014/04/17 职场文书
《作风建设永远在路上》心得体会
2016/01/21 职场文书