Long PHP 脚本运行多次

Long PHP script runs multiple times

我有一个每天早上都与产品数据同步的产品数据库。

过程很清楚:

如果我查询的项目数量较少,但将其限制为例如 500 个随机产品,一切都会顺利进行。但是当我查询所有产品时,我的脚本有时会出错并开始循环多次。几个小时后,我仍然看到我的日志文件在增长并且产品在添加。

我检查了所有我能想到的,例如:

之所以特别奇怪,是因为它有时是对的,有时却不是。这可能是内存问题吗?

编辑 wget -q -O /dev/null http://example.eu/xxxxx/cron.php?operation=sync 它在 webmin 中调用了特定的时间和分钟

代码有几百行...

谢谢

你有:

  • max_execution_time 禁用。只要需要,您的脚本就不会结束,直到该过程完成为止。
  • memory_limit 禁用。内存中存储的数据量没有限制。

500 条记录已顺利完成。这表明脚本在下一次 cronjob 迭代之前完成了它的过程。例如,如果您的 cron 运行 每小时执行一次,则 500 条记录将在不到一个小时内处理完毕。

如果你有一个要处理大量记录的cronjob,那么考虑给进程添加锁机制。只允许脚本运行一次,等上一个过程完成后再开始。

您可以在执行 php 脚本之前创建脚本锁作为 shell 脚本的一部分。或者,如果您无法访问您的服务器,您可以在 php 脚本中使用数据库锁定,类似这样。

class ProductCronJob
{
    protected $lockValue;

    public function run()
    {
        // Obtain a lock
        if ($this->obtainLock()) {
            // Run your script if you have valid lock
            $this->syncProducts();

            // Release the lock on complete
            $this->releaseLock();
        }
    }

    protected function syncProducts()
    {
        // your long running script
    }

    protected function obtainLock()
    {
        $time = new \DateTime;
        $timestamp = $time->getTimestamp();
        $this->lockValue = $timestamp . '_syncProducts';

        $db = JFactory::getDbo();

        $lock = [
            'lock'         => $this->lockValue,
            'timemodified' => $timestamp
        ];
        // lock = '0' indicate that the cronjob is not active.
        // Update #__cronlock set lock = '', timemodified = '' where name = 'syncProducts' and lock = '0'
//        $result = $db->updateObject('#__cronlock', $lock, 'id');

//        $lock = SELECT * FROM #__cronlock where name = 'syncProducts';

        if ($lock !== false && (string)$lock !== (string)$this->lockValue) {
            // Currently there is an active process - can't start a new one

            return false;

            // You can return false as above or add extra logic as below

            // Check the current lock age - how long its been running for
//            $diff = $timestamp - $lock['timemodified'];
//            if ($diff >= 25200) {
//                // The current script is active for 7 hours.
//                // You can change 25200 to any number of seconds you want.
//                // Here you can send notification email to site administrator.
//                // ...
//            }
        }

        return true;
    }

    protected function releaseLock()
    {
        // Update #__cronlock set lock = '0' where name = 'syncProducts'
    }
}

我看到两种可能性: - chron 更频繁地调用脚本 - 脚本以某种方式花费了太长时间。

您可以尝试估计循环的单次迭代所花费的时间。 这可以用 time() 来完成。也许结果令人惊讶,也许不是。你也可能得到结果的数量。将两者相乘,这样您就可以估计该过程需要多长时间。

$productsToSync = $db->loadObjectList();

foreach ($productsToSync AS $product) {

您似乎将每个结果都加载到一个数组中。这不适用于大型数据库,因为显然一百万行不会适合内存。你应该一次只得到一个结果。对于 mysql,有些方法只能一次从资源中获取一件事,我希望你的方法也允许这样做。

我还看到您在循环的每次迭代中执行另一个查询。这是我尽量避免的事情。也许您可以将其移至第一个查询结束后并在一个大查询中执行所有这些操作? otoh 这可能会影响我的第一个建议。

另外,如果出现问题,请在调试时保持偏执。尽可能多地测量。当它是一个性能问题时,尽可能多的时间。把时间放在你的日志文件中。通常你会发现瓶颈。

您的脚本 运行 已经运行了很长一段时间 (~45m) 并且 wget 认为它是 "timing out" 因为您没有 return 任何数据。默认情况下,wget 的超时值为 900 秒,重试次数为 20。因此,首先您应该更改 wget 命令以防止出现这种情况:

wget --tries=0 --timeout=0 -q -O /dev/null http://example.eu/xxxxx/cron.php?operation=sync

现在删除超时可能会导致其他问题,因此您可以从脚本发送(并且 flush 以强制网络服务器发送它)数据以确保 wget 不认为脚本 "timed out",每 1000 次循环或类似的东西。将其视为进度条...

请记住,当 运行 时间接近您的经期时,您会遇到一个问题,因为 2 个 crons 将 运行 并行。您应该优化您的流程 and/or 也许有锁定机制?

我自己解决了这个问题。感谢大家的回复!

我的 MySQL 超时了,这就是问题所在。一旦我添加:

    ini_set('mysql.connect_timeout', 14400);
    ini_set('default_socket_timeout', 14400);

我的脚本问题停止了。我真的希望这对某人有帮助。我会对所有锁定答案投赞成票,因为它们非常有帮助!