分叉的PHP进程中的连接错误

时间:2019-07-03 21:31:14

标签: php mongodb pcntl

我有一个 PHP脚本,该脚本从MongoDB中提取了N个文档,将该进程派生到K个子PHP进程中,每个进程对每个文档都做一些事情,然后尝试更新文档的信息(请参见下面的代码) )。

在我的本地环境(Docker上,一切都很酷,但是在服务器上(那里没有Docker),有时在循环中会发生奇怪的事情...

随机地,所有派生的进程都无法连接到MongoDB。 updateOne命令返回错误:

  

“无法发送带有数据库“ databasename”的“ update”命令:来自服务器的无效回复。在第158行的/vendor/mongodb/mongodb/src/Operation/Update.php中。” < / p>

仅在一个(或几个)随机循环迭代中,所有进程同时发生这种情况。当每个过程进行另一个迭代(获取下一个文档)时,一切都可以了。我做了5次尝试写入MongoDB。

每次尝试都比上一次延迟 +1秒,因此,如果发现任何异常,则第一次尝试会立即进行-等待一秒钟,然后重试,下一次尝试将是2秒等等。但这无济于事,这5次尝试都失败了。

这不是mongoDB的问题,它的日志为空,并且在发生错误时甚至没有从PHP接收任何信息。

我也承认,我运行的进程越多并发-错误发生的频率就越高。

这也不是服务器资源问题,当发生错误时,一半的RAM(4兆字节)可用,CPU可以用一半的功率工作。

也许PHP为此有一些配置?一些内存限制或其他...

我使用 PHP v 7.1.30 MongoDB v 3.2.16 PHP软件包mongodb / mongodb v 1.1.2

<?php

$processesAmount = 5;
$documents = $this->mongoResource->getDocuments();

for ($processNumber = 0; $processNumber < $processesAmount; $processNumber++) {
    // create child process
    $pid = pcntl_fork();

    // do not create new processes in child processes
    if ($pid === 0) {
        break;
    }

    if ($pid === -1) {
       // some errors catching staff here...
    }
    else if ($pid === 0) {
        // create new MongoDB connection
    }
    else { 
        // Protect against Zombie children
        // main process waits before all child processes end
        for ($i = 0; $i < $processesAmount; $i++) {
            pcntl_wait($status);
        }
        return null;
    }

    // spread documents to each process without duplicates
    for ($i = $processNumber; $i < count($documents); $i += $processesAmount) {
        $newDocumentData = $this->doSomeStaffWithDocument($documents[$i]);
        $this->mongoResource->updateDocument($documents[$i], $newDocumentData);
    }
}

1 个答案:

答案 0 :(得分:0)

这里可能存在很多问题,一个问题是所有进程都共享1个DB连接,而第一个连接就是断开连接并终止所有连接。在此处查看文档中的第二个示例:https://www.php.net/manual/en/ref.pcntl.php

如果这没有帮助,那么我阅读代码的方式“扩展”部分就是每个过程都发生的事情,而本应只发生一次。您不应该像下面那样在子部分中放置“作品”吗?

$processesAmount = 5;
$documents = $this->mongoResource->getDocuments();
$numDocs = count($documents);
$i = 0;
$children = [];

    for ($processNumber = 0; $processNumber < $processesAmount; $processNumber++) {
        // create child
        $pid = pcntl_fork();

        if ($pid === -1) {
           // some errors catching staff here...
        } else if ($pid) {
            //parent
            $children[] = $pid;
        } else { 
            //child
           while (!empty($documents) && $i <= $numDocs) {
               $i += $processNumber;
               $doc = $documents[$i] ?? null;
               unset($documents[$i]);
               $newDocumentData = $this->doSomeStaffWithDocument($doc);
               $this->mongoResource->updateDocument($doc, $newDocumentData);
           }
        }
    }

    //protect against zombies and wait for parent
    //children is always empty unless in parent
    while (!empty($children)) {  

        foreach ($children as $key => $pid) {

            $status = null;

            $res = pcntl_waitpid($pid, $status, WNOHANG);

            if ($res == -1 || $res > 0) {   //if the process has already exited
                unset($children[$key]);
            }

        }

    }

}