/* 记录两个在php7升级过程中踩的坑。*/
一、扩展的不兼容
问题现象:
1、升级完php7的机器中,有几台从曲线、指标上看,性能反而不如php5
2、查看其中一台,nginx access日志里大量502错误
3、fpm产生大量慢日志,错误日志里记录着类似:“WARNING: [pool www] child 31570 exited on signal 11 (SIGSEGV - core dumped) after 490.850482 seconds from start”
初步结论:
看起来问题是出在fpm,并且间接性core dump了。遗憾的是,系统配置并没有保留core文件。
复现问题:
1、联系运维,摘掉有问题的线上机流量
2、要一台机器临时root权限,修改机器配置,以保留core文件
3、重启fpm,很快就有大量的core文件了。(改回配置,重启fpm)
4、进行gdb调试,如下图
从调用栈可以看到,最后执行Memcached::addServers,调用libmemcached时出错了。
可以基本判断出是使用缓存出问题了。
网上查相关资料,有解释是这样说的:memcached长连接的问题 看来似乎还是得从业务入手。
定位问题:
分析nginx access日志,发现主要集中在两类请求
1) xxxx/post/detail/?puid=2810963318
2) xxxx/xiaoqu/detail/?xiaoquId=1579&fields=public%2Cstat%2Cfacility
第一类请求正常返回,第二类请求有很多的502。配置host在摘掉流量的机器上调试,发现大量刷页面时可以复现问题。
追踪代码,发现该业务使用了公司的wcache服务(分布式缓存),而wcache使用memcached协议。将cache注释,发现问题不再复现。即可确定是wcache的问题。原因是,service的三台机器wcache客户端未更新为php7版本,更新后即解决问题。水平有限,未彻底跟进旧版本的问题原因。
经验总结:
1、扩展的问题,一般会导致fpm出错,甚至导致core。这时用gdb看一下,就可以知道是哪个扩展出问题了。网上查查这个扩展相关的信息
2、从nginx access日志入手,则可以知道是哪类业务出错多,便于复现问题,断点调试。
二、业务的问题
问题现象:
1、升级完php7的机器中,有几台从曲线、指标上看,提升不大,感觉有异常
2、nginx access日志里大量499错误
3、fpm大量慢日志,错误日志里记录着类似:WARNING: [pool www] child 29682 exited on signal 15 (SIGTERM) after 58.730780 seconds from start
初步结论:
从fpm日志看,这是php进程执行过慢,超时导致fpm进程终止。
定位问题:
从nginx access日志里看,499错误集中在类似:xxx/fang5/2991994508x.htm的请求上,是房源详情页的展示。但是,php5的机器上access日志有499的错误,但是fpm没有SIGTERM的错误。
通过打断点的方式,发现在详情页里,有一处业务,在使用fsocketopen时,对应服务已经下线,超时设置为800ms,导致一定超时,并且重试1次。
但是,fpm配置里,进程超时时间设置为30s,1.6s也远远达不到,最多是页面卡。况且,业务代码在php5和php7是一样的,5下为何没有问题呢,解释不通。
而且,fsocketopen本身实现是异步的。
ptrace
修改fpm配置,只启动2个子进程进行处理。ptrace跟踪任务,看看一次请求php究竟做了些啥。狂刷页面,终于问题复现了。
分析日志发现,有大量的socket使用(连接、收发数据),和nanosleep()系统调用。并且nanosleep都是伴随在socket不可用时产生的。
这样就可以分析出,是底层建立网络连接时,有很多连接不可用,调用epoll,并等待。最终导致进程执行时间过长。这么看来,真的是前面断点处使用fsocketopen问题咯?但是30s超时真解释不通。
分析ptrace日志,发现socket连接失败,集中在某几个ip和端口。grep业务代码,没找到ip和端口。这时就需要找运维同学看了。
这些ip和端口对应的服务,发现是session服务。
同时,在ptrace日志,也发现,确实写着mem.ses的字眼。那么基本可以确定,是session使用出问题了。把业务底层的session使用注释,发现问题不再复现。
问题结论:
看了一下session的实现,使用的memcache分布式存储,只在进程执行结束时,释放session。那么,当大量请求、进程执行慢时,session锁竞争会变得非常严重,最终导致php进程的超时。
解决办法是,减少业务上的超时接口,例如前面所述业务fsocketopen的1.6s。并建议使用session时,获取完数据就手动释放,减少锁。
经验总结:
配置fpm,起一两个进程,用ptrace跟踪定位问题非常方便。