记一次php高负载问题排查经历

在最近一次项目中,后台跑了deamon来处理任务。之前一直没什么问题,在一次增加任务处理时间等监控上报后,deamon的机器出现高负载,平均在20以上,任务出现积压。

第一步先使用strace查看系统调用情况:

strace -tt -T -c -p 31420

技术分享

strace -c发现,系统时间主要耗费在rt_sigprocmask上,该函数是系统的信号屏蔽函数,一般使用在信号处理程序中,对信号进行屏蔽,保证信号处理程序不被打断。rt_sigprocmasklinux的系统调用函数,不对外直接调用,而使用glibc对其的封装:sigprocmask.

进一步starce调用实际情况:

技术分享

rt_singprocmask调用有一个特征:设置屏蔽字后紧接着恢复屏蔽字,可以联想到这有可能php的信号处理函数中调用的。

php源码中搜索sigprocmask,发现php源码中调用singprocmask的地方主要在pnctl扩展中,在deamon程序使用了pnctl扩展创建和管理子进程。通过先阻塞信号后恢复信号的特征,定位到php的信号处理函数:pcntl_signal_dispatch()中。

接着通过加日志验证我们的想法,确认确实在频繁调用该函数。由此引出了对php信号处理机制的分析。

 

限于篇幅,具体分析过程省略,这里给出分析的结果:

  1. phptick机制:

phpmanultick的解释是:

A tick is an event that occurs for every N low-level tickable statements executed by the parser within the declare block. The value for N is specified using ticks=N within the declare block‘s directive section.

Not all statements are tickable. Typically, condition expressions and argument expressions are not tickable.

The event(s) that occur on each tick are specified using the register_tick_function(). See the example below for more details. Note that more than one event can occur for each tick.

大概意思是:tickregister_tick_function配合,每执行tick=nn个底层指令,就触发执行register_tick_function注册的事件。

   2. php对信号的处理:

Php对进程和信号的管理控制通过pcntl扩展实现,pcntl使用了tick机制作为signalcallback机制,你可以在php中允许callback的地方使用declare允许callback发生。

通过对pcntl源码分析,pcntl的信号处理机制是这样的:

1),信号注册

技术分享

 

2),信号处理:

技术分享

 

分析到这里,可以看到:在php中,根据declare声明的tick值来定期调用,当tick值配置较低时(本程序中定义ticks=1),会大量触发信号处理程序,该程序中调用rt_sigprocmask导致浪费大量的cpu,机器负载变高。

那么php为什么要采用这种信号处理机制呢?

 

查看在php manual时,发现这样的介绍:

PCNTL now uses ticks as the signal handle callback mechanism, which is much faster than the previous mechanism. This change follows the same semantics as using "user ticks". You use the declare() statement to specify the locations in your program where callbacks are allowed to occur. This allows you to minimize the overhead of handling asynchronous events. In the past, compiling PHP with pcntl enabled would always incur this overhead, whether or not your script actually used pcntl.

重点关注:You use the declare() statement to specify the locations in your program where callbacks are allowed to occur. 意思是可以指定需要捕获信号的代码区域,区域外则不进行信号处理。

 

由上可以总结出修改问题的办法:

1),通过declare制定应用程序捕捉信号的区域,

2),对于不需要进行信号处理的程序,不声明declearticks=n),由此不会触发调用信号处理程序;需要处理的程可以手动调用信号处理程序pcntl_signal_dispatch代替php的定时检测。

 

经过验证,采用以上任何一种办法都可以解决问题。机器负载降到正常值。

 

总结:

1),php采用类似linux内核中断处理机制(硬中断+软终端)机制处理信号,需要注意检测信号的频次,避免cpu性能浪费。

2),因为在cgi中不建议使用pcntl扩展管理创建进程,该机制不会对webservercgi产生影响

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。