控制台脚本。使用cUrl时,子进程变为僵尸

时间:2018-04-26 13:17:07

标签: php unix curl multiprocessing

控制台脚本从外部API执行数据导入。用于增强由pcntl_fork命令创建的并发进程中执行的导入加载。 对于与API的通信,使用cUrl。通过https协议执行的通信。

由于一些不明确的原因,一些孩子会定期成为僵尸。控制台中没有错误/警告/通知,也没有写入日志。错误级别已正确配置。

经过调查我认为卷曲扩展存在问题,因为没有它,假连接,没有问题。 此外,如果在单一过程模式下运行导入 - 根本没有问题。

PHP:7.2.4, 操作系统:Debian 9, 卷曲:7.59.0(x86_64-pc-linux-gnu)libcurl / 7.47.0 OpenSSL / 1.0.2g zlib / 1.2.8 libidn / 1.32 librtmp / 2.3

也许有人遇到类似问题或者知道这种奇怪行为的可能原因?

儿童逻辑的伪代码样本(儿童的主要部分表示):

while (true) {
        $socket->writeRawString(Signal::MESSAGE_REQUEST_DATA);
        $response = $socket->readRawString();
        if (Signal::MESSAGE_TERMINATE_PROCESS === $response) {
            break;
        }
        $response = json_decode($response, true);
        if (empty($response) || empty($response['deltaId'])) {
            continue;
        }
        $delta = $this->providerConnection->getChanges($response['deltaId']);
        if(empty($delta)) {
            continue;
        }
        $xmlReader = new \XMLReader();
        $xmlReader->XML($delta);
        $xmlReader->read();
        $xmlReader->read();
        $hasNext = true;
        while ($hasNext && 'updated' !== $xmlReader->name) {
            $hasNext = $xmlReader->next();
        }
        if ('updated' !== $xmlReader->name) {
            throw new \RuntimeException('Deltas file do not contain updated date.');
        }
        if (strtotime($xmlReader->readString()) < $endDateTimestamp) {
            $socket->writeRawString(self::SIGNAL_END_DATE_REACHED);
            continue;
        }
    }
    posix_kill(\posix_getpid(), SIGTERM);

在通过cUrl执行的providerConnection->getChanges($response['deltaId']);请求中。对于使用Php cUrl class扩展名

的cUrl工作

2 个答案:

答案 0 :(得分:0)

正如我的评论中所提到的,你的问题可能是,父进程需要收集死亡/完成的子进程,否则它们仍然是僵尸。

第一个解决方案:

在父级中安装信号处理程序。像这样:

pcntl_signal(SIGCHLD, [$this, 'handleSignals']);

使用可能如下所示的信号处理程序:

/**
 * @param integer $signal
 */
public function handleSignals($signal) {
    switch($signal) {
        case SIGCHLD:
                do { 
                    $pid = pcntl_wait($status, WNOHANG);
                } while($pid > 0);
            break;
        default:
            //Nothing to do
    }
}

我通常会存储分叉儿童的pids并使用pcntl_waitpid单独检查它们,但这可以帮助你。

第二个解决方案:

如果父级不需要等待所有子任务完成,则使用双叉来生成子进程。双叉看起来像这样:

$pid = pcntl_fork();
if ($pid == -1) handleError();
elseif ($pid ==  0) { // child
    $pid = pcntl_fork(); 
    if ($pid == -1) handleChildError();
    elseif($pid == 0) { // second level child
        exit(startWork()); // init will take over this process
    }  
    // exit first level child
    exit(0);
} else {
    // parent, wait for first level child
    pcntl_wait($pid, $status); // forked child returns almost immediatly, so blocking wait is in order
}

答案 1 :(得分:0)

我放弃使用cUrl完成我的任务。今天我用StreamHandler而不是cUrl切换到Guzzle,它解决了我所有的问题。

我想,由于cUrl中的一些内部错误,系统正在杀死我的子进程。

这不是我的问题的答案。对于那些可能遇到类似问题的人来说,这只是我的问题的解决方法。

主题仍然可以提供可能的建议/解释。