在单独的进程中运行PHP更长的时间


Run PHP for longer time in separate processes

我有一个目录,可以包含CSV文件,通过我需要导入数据库的服务。这些CSV文件每个有1000行,可以是10到150个文件。

我想把所有这些CSV文件的数据插入数据库。问题是PHP因为超时问题而死亡,因为即使我使用set_time_limit(0),服务器(siteground.com)也会施加限制。下面是代码:

// just in case even though console script should not have problem
ini_set('memory_limit', '-1');
ini_set('max_input_time', '-1');
ini_set('max_execution_time', '0');
set_time_limit(0);
ignore_user_abort(1);
///////////////////////////////////////////////////////////////////
function getRow()
{
    $files = glob('someFolder/*.csv');
    foreach ($files as $csvFile) {
        $fh = fopen($csvFile, 'r');
        $count = 0;
        while ($row = fgetcsv($fh)) {
            $count++;
            // skip header
            if ($count === 1) {
                continue;
            }
            // make sure count of header and actual row is same
            if (count($this->headerRow) !== count($row)) {
                continue;
            }
            $rowWithHeader = array_combine($this->headerRow, $row);
            yield $rowWithHeader;
        }
    }
}
foreach(getRow() as $row) {
   // fix row
   // now insert in database
}

这实际上是通过artisan运行的命令(我使用Laravel)。我知道CLI没有时间限制,但由于某些原因,并不是所有的CSV文件都被导入,处理在某个时间点结束。

所以我的问题是有没有办法调用单独的PHP进程为每个CSV文件存在于一个目录?或者其他方式这样我就可以导入所有CSV文件而不会出现任何问题比如PHP的generator,

您可以使用一些bash魔法。重构脚本,使其只处理一个文件。要处理的文件是脚本的一个参数,可以使用$argv访问它。

<?php
// just in case even though console script should not have problem
ini_set('memory_limit', '-1');
ini_set('max_input_time', '-1');
ini_set('max_execution_time', '0');
set_time_limit(0);
ignore_user_abort(1);
$file = $argv[1]; // file is the first and only argument to the script
///////////////////////////////////////////////////////////////////
function getRow($csvFile)
{
    $fh = fopen($csvFile, 'r');
    $count = 0;
    while ($row = fgetcsv($fh)) {
        $count++;
        // skip header
        if ($count === 1) {
            continue;
        }
        // make sure count of header and actual row is same
        if (count($this->headerRow) !== count($row)) {
            continue;
        }
        $rowWithHeader = array_combine($this->headerRow, $row);
        yield $rowWithHeader;
    }
}
foreach(getRow($file) as $row) {
   // fix row
   // now insert in database
}
现在,像这样调用脚本:
for file in `ls /path/to/folder | grep csv`; do php /path/to/your/script.php /path/to/folder/$file; done

这将为/path/to/folder

中的每个.csv文件执行脚本。

最好的方法是在一个php进程中处理有限数量的文件。例如,您可以从10个文件开始,处理它们,标记为已删除(移动到包含已处理文件的文件夹),然后停止该进程。然后启动一个新进程导入另外10个文件,以此类推。在Laravel中,如果另一个进程正在运行,你可以说不要为一个特定的命令启动多个进程。Laravel的命令如下:

$schedule->command("your job")->everyMinute()->withoutOverlapping();

如果你使用这种方法,你可以确保所有的文件将被处理特定的时间,他们不会消耗太多的资源被杀死。

如果你的主机提供商允许cron作业,他们没有超时限制。

对于繁重和长时间的任务,它们应该比手动调用函数更适合工作,因为如果多次调用该方法可能会导致巨大的问题。