我们正在运行一个PHP守护进程,它查看队列,接收worker作业并生成worker来处理它。工人自己在进行之前获得一个特定位置的锁。
我们将Daemon作为nohup后台进程生成。
整个体系结构似乎是工作的,除了当我们不得不杀死进程时,无论出于什么原因。如果我们使用-9杀死它们,就没有办法在工作进程中捕获它并在死亡之前释放锁。
如果我们使用小于-9的参数(如TERM或HUP),则守护进程或工作进程似乎都不会接收到它。
有人用更好的方法解决了这个问题吗?(ps: BTW,由于其他考虑,我们可能无法改变我们的实现语言,所以请只考虑基于PHP的解决方案)
我也曾经有过类似的问题。让我解释一下。我有一个像下载程序一样工作的php"守护进程"。它定期访问提要,并从网上下载(大量)内容。这个守护进程必须在特定的时间停止,比如早上的0500,以防止它在白天使用整个带宽。我决定使用cronjob在0500向守护进程发送SIGTERM。
在守护进程中我有以下代码:
pcntl_signal(SIGTERM, array($this, 'signal_handler'));
其中signal_handler
是这样的:
public function signal_handler($signal) {
// some cleanup code
exit(1);
}
不幸的是这不起作用:|
我花了很长时间才弄清楚发生了什么事。我弄清楚的第一件事是,我必须在init上调用pcntl_signal_dispatch()
方法来启用信号调度。引用自文档(注释):
如果你把PHP作为CLI和一个"守护进程"(即在一个循环中)运行,这个函数必须在每个循环中调用,以检查是否有新的信号正在等待调度。
好的,到目前为止,它似乎有效。但我很快意识到,在某些条件下,即使这样也不会像预期的那样起作用。有时守护进程只能由kill -9
来停止——就像以前一样。: |
那么问题是什么?答:我的程序调用wget
,通过shell_exec
下载文件。问题是,shell_exec()
阻塞等待子进程终止。在此阻塞等待期间,没有信号处理完成,进程只能使用SIGKILL终止-这是困难的。还有一个问题是,子进程必须一个接一个地终止,因为它们在杀死父进程后成为僵尸进程。
我的解决方案是使用proc_open()
执行子进程,并在它的输出上使用stream_select()
进行非阻塞IO。
现在它像一个魅力。:)如果你需要进一步的信息,请不要犹豫,留下你的评论。
注意如果你正在使用PHP <那么你必须使用'>
declare(ticks=1);
代替pcntl_signal_dispatch()
。您可以参考pcntl_signal()
的文档。但是如果可能的话你应该升级到PHP>= 5.3
只需添加刻度,问题就解决了:
// tick use required as of PHP 4.3.0
declare(ticks = 1);
把这个单独留下会导致我的代码不能工作。
*(不幸的是,pcntl_signal的文档没有以更引人注目的方式提到它)*
您需要捕获信号(SIGTERM)。这可以通过函数pcntl_signal实现。这将为您提供在调用exit之前执行任何必要函数的选项。