【问题标题】:How to handle pcntl_fork(): Error 35?如何处理 pcntl_fork():错误 35?
【发布时间】:2017-04-06 11:01:55
【问题描述】:

我有 php7 CLI 守护程序,它串行解析文件大小超过 50M 的 json。我正在尝试使用带有 pcntl_fork() 的单独进程将每 1000 个已解析数据条目保存到 mysql,并且对于 ~200k 行它工作正常。

然后我得到 pcntl_fork():错误 35

我认为这是因为 mysql 插入变得比解析慢,这导致生成越来越多的分叉,直到 CentOS 6.3 无法再处理它。

有没有办法捕捉这个错误以诉诸单进程解析和保存?或者有没有办法检查子进程计数?

【问题讨论】:

  • 35 是EGAIN,这意味着您已达到进程限制。也许您不会在完成后调用pcntl_wait 来清理进程?
  • centos中fork进程限制的最大数量是多少?因为那样它应该是可计算的,200k 记录将垃圾邮件 +/- 200 个分叉。 “或者有没有办法检查子进程计数”这可以通过保留 pid 来手动完成。一个更好的方法是启动多个守护进程来获取一个工作或使用 proc_open php.net/proc_open 运行它
  • 那么为什么分叉是解决问题的方法呢?您正在解析一些数据,然后使用单独的进程同时访问 MySQL - 为什么这会成为解决方案?在同一进程中使用事务有什么问题?你显然在想,如果你分叉成 N 个进程,它会快 N 倍,但它不会。现在我们有一个糟糕的解决方案的问题和一个糟糕的解决方案的答案。如果您希望插入速度更快,请将这 1000 行分组到同一个事务中。这将花费最少的 I/O 进行写入。
  • @Mjh 我已经在内存中为大量 SQL 插入分组数据,但是进行 sql 插入需要时间。我希望这段时间花在实际的输入流处理上。将其卸载到单独的进程对我来说效果很好,每秒可实现 1.5k 次插入
  • 从字面上将它分组到内存服务器中 - 没有目的。有两个过程,一个用于输入,一个用于插入是解决此问题的方法。但是,您的项目、您的代码以及最终是您的时间。最糟糕的是,有人可能认为这是要走的路——事实并非如此。祝你好运。

标签: php mysql json


【解决方案1】:

这是我根据@Sander Visser 评论所做的解决方案。关键部分是检查现有流程,如果它们太多,则诉诸相同的流程

class serialJsonReader{

const MAX_CHILD_PROCESSES = 50;
private $child_processes=[]; //will store alive child PIDs

private function flushCachedDataToStore() {

//resort to single process
    if (count($this->child_processes) > self::MAX_CHILD_PROCESSES) {
        $this->checkChildProcesses();

        $this->storeCollectedData() //main work here
    }

//use as much as possible
    else {
        $pid = pcntl_fork();
        if (!$pid) {
            $this->storeCollectedData(); //main work here
            exit();
        }
        elseif ($pid == -1) {
            die('could not fork');
        }
        else {
            $this->child_processes[] = $pid;
            $this->checkChildProcesses();
        }
    }
}

private function checkChildProcesses() {
    if (count($this->child_processes) > self::MAX_CHILD_PROCESSES) {
        foreach ($this->child_processes as $key => $pid) {
            $res = pcntl_waitpid($pid, $status, WNOHANG);

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

【讨论】:

猜你喜欢
  • 2013-05-25
  • 2017-05-21
  • 2011-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多