【问题标题】:Run PHP for longer time in separate processes在单独的进程中运行 PHP 更长时间
【发布时间】:2016-09-04 19:33:38
【问题描述】:

我有一个目录,其中可以包含来自我需要导入数据库的服务的 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 文件都被导入并且进程在某个时间点结束。

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

【问题讨论】:

  • 如果托管服务提供商希望脚本只运行有限的时间,那么他可以这样做。即使通过 CLI 运行 PHP 也无济于事。他可以直接杀死进程。
  • 为什么在连接到远程数据库时不从 localhost 运行 PHP 脚本?在本地,您可以为脚本设置没有时间限制。
  • @RobertTrzebiński:它实际上必须在服务器上运行。
  • 丢人,所以你需要一次处理一个文件,或者如果这还不够,请更改托管计划。
  • 请记住:如果运行时间过长,您可以在 Web 服务器(nginx、Apache)或 PHP 池(fpm、fastcgi)中设置超时,这也会杀死 PHP。不跑过cli,不只是php.ini。

标签: php performance laravel csv


【解决方案1】:

你可以做一些 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 文件执行脚本

【讨论】:

    【解决方案2】:

    最好的方法是每个 php 进程处理有限数量的文件。例如,您可以从 10 个(根据经验计算文件数)文件开始,处理它们,标记为已删除(移动到已处理文件的文件夹)并停止该过程。之后开始一个新过程以导入另外 10 个文件,依此类推。在 Laravel 中,如果另一个进程已经在工作,你可以说不要为一个特定的命令启动多个进程。 Laravel 的命令如下:

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

    如果您使用这种方法,您可以确保所有文件都将在特定时间处理,并且不会消耗太多资源而被杀死。

    【讨论】:

      【解决方案3】:

      如果您的托管服务提供商允许 cron 作业,他们没有超时限制。

      此外,它们应该比手动调用函数来完成繁重和冗长的任务更适合这项工作,因为如果多次调用该方法可能会出现巨大的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        • 1970-01-01
        • 2018-02-05
        • 1970-01-01
        • 2015-04-18
        • 1970-01-01
        相关资源
        最近更新 更多