我们正在运行一个PHP守护程序,它会查看队列,接收工作程序作业并生成工作程序来处理它。在继续之前,工人自己获得对特定位置的锁定。
我们将守护进程产生为nohup后台进程。
这整个架构似乎都有效,除非我们因为某种原因必须杀死进程。如果我们使用-9杀死它们,则无法将其捕获到工作进程中并在死亡之前释放锁。
如果我们使用小于-9的任何东西(如TERM或HUP),守护程序或工作进程似乎都没有收到它。
有没有人以更好的方式解决这个问题?
(ps:BTW,由于其他考虑因素,我们可能无法更改我们的实现语言,因此请仅考虑基于PHP的解决方案)
答案 0 :(得分:3)
我也有过相关的问题。让我解释。我有一个php'守护进程',就像一个下载程序。它定期访问订阅源并从网上下载(laaaarge)内容。守护进程必须在某个时间停止,让我们说早上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()
来启用信号调度。引用文档(comments):
如果您将PHP作为CLI和“守护程序”运行(即在循环中),则必须在每个循环中调用此函数以检查新信号是否在等待调度。
好的,到目前为止,似乎有效。但我很快意识到,在某些条件下,即使这样也无法按预期工作。有时守护进程只能被kill -9
停止 - 和以前一样。 :|
那么问题是什么?答案:我的程序名为wget
,通过shell_exec
下载文件。问题是,shell_exec()
阻塞等待子进程终止。在此阻塞等待期间,没有信号处理完成,该过程只能使用SIGKILL终止 - 这很难。还有一个问题是,在杀死父亲之后,子进程必须逐个终止,因为它们变成了僵尸进程。
我的解决方案是使用proc_open()
执行子进程,并使用stream_select()
输出非阻塞IO。
现在它就像一个魅力。 :)如果您需要更多信息,请随时发表评论。
注意如果您正在使用PHP< 5.3那么你将不得不使用`
declare(ticks=1);
而不是pcntl_signal_dispatch()
。您可以参考pcntl_signal()
的文档。但是如果可能的话,你应该升级到PHP> = 5.3
答案 1 :(得分:1)
只需添加刻度即可解决问题:
// tick use required as of PHP 4.3.0
declare(ticks = 1);
单独留下这导致我的代码无法正常工作。
*(很遗憾,pcntl_signal的文档并未提及更多关注方式。)*
答案 2 :(得分:0)
您需要捕获信号(SIGTERM)。这可以通过函数pcntl_signal来实现。这将为您提供在调用exit之前执行任何必要功能的选项。