我正在尝试创建一个在后台运行并分叉子进程的PHP脚本。 (我知道可能会爆炸服务器;还有一些超出此问题范围的额外保护措施)
简而言之,代码的工作原理如下:
$pids = array();
$ok = true;
while ($ok)
{
$job = $pheanstalk->watch('jobs')->ignore('default')->reserve();
echo 'Reserved job #' . $job->getId();
$pids[$job->getId()] = pcntl_fork();
if(!$pids[$job->getId()]) {
doStuff();
$pheanstalk->delete($job);
exit();
}
}
问题是,一旦我分叉了这个过程,我就会收到错误:
Reserved job #0
PHP Fatal error: Uncaught exception 'Pheanstalk_Exception_ServerException' with message 'Cannot delete job 0: NOT_FOUND'
我的问题是,pheanstalk如何返回没有ID且没有负载的作业?一旦我把它拆开,几乎感觉$ pheanstalk被损坏了。如果我删除分叉,一切正常。 (虽然它必须等待每个过程)
答案 0 :(得分:1)
在删除pheanstalk作业之前放置此if条件:
if ($job) $pheanstalk->delete($job);
这是因为你的文件的另一个php实例很可能已经在代码到达这个地方之前删除了那个作业。 (另一个实例仍然可以使用reserve()检索此作业,直到从队列中删除作业为止。
答案 1 :(得分:1)
您遇到此问题的原因是主要流程保留了该作业。调用pcntl_fork()
之后实际上有一个$worker
变量的副本,因此主进程锁定了作业,第二个作业在尝试删除它时表示它没有存在(或者在这种情况下,它由另一个进程保留)。下面的代码通过创建一个新的worker来处理它,然后在main worker上释放作业,并试图在第二个worker上找到它。
# worker for the main process
$worker = new \Pheanstalk\Pheanstalk($host, $port, $timeout, $persistent);
$pid = -1;
# seek out new jobs
while ($job = $worker->watch('queue_caller')->reserve()) {
# make sure pcntl is installed & enabled
if (function_exists('pcntl_fork')) {
# create a child process
$pid = pcntl_fork();
}
if ($pid === -1) {
# code will run in single threaded mode
} elseif ($pid !== 0) {
# parent process
# release the job so it can be picked up by the fork
$worker->release($job);
# short wait (1/20000th second) to ensure the fork executes first
# adjust this value to what is appropriate for your environment
usleep(50);
# clear out zombie processes after they're completed
pcntl_waitpid(0, $pidStatus, WNOHANG);
# go back to looking for jobs
continue;
} else {
# child worker is needed, because it has to own the job to delete it
/** @var Pheanstalk $worker */
$worker = new \Pheanstalk\Pheanstalk($host, $port, $timeout, $persistent);
# only look for jobs for 1 second, in theory it should always find something
$job = $worker->watch('queue_caller')->reserve(1);
if (false === $job) {
# for some reason there is no job
# terminate the child process with an error code
exit(1);
}
}
/** Start your code **/
do_something();
/** End your code **/
# delete the job from the queue
$worker->delete($job);
# only terminate if it's the child process
if ($pid === 0) {
# terminate the child process with success code
exit(0);
}
}