将PHP进程作为守护进程运行,同时在后台安全地杀死它


Running a PHP process as a daemon while safely killing it from background

我们正在运行一个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之前执行任何必要函数的选项。