【问题标题】:PHP execution gets stuck when initializing a class (with a simple constructor)初始化类时 PHP 执行卡住(使用简单的构造函数)
【发布时间】:2021-04-01 15:26:18
【问题描述】:

我有一个以 Laravel 作为后端的预订系统 web 应用程序(一切正常,授权、请求、编辑用户详细信息和其他对象也是如此)。

我有编辑营业时间的特殊设置。我创建了一个名为 Intervals 的特殊类,它可以帮助我计算可用小时数,如果给定时间戳可用,则根据用户设置返回所有不可用天数等。

问题

当我尝试初始化 Intervals 类时,问题就来了,例如:

Log::info("before Intervals"); // gets logged
$opened = new Intervals();
Log::info("after Intervals"); // doesn't get logged

正如你在这里看到的,它有一个非常简单的构造函数,没有昂贵的计算和函数调用(除了 Laravel 日志函数 - 日志不是导致问题的原因)

public function __construct (array $from = [], array $to = []) {
    Log::info("__construct: $from, $to"); // doesn't get logged
    if (count($from) !== count($to)) {
        $this->from = [];
        $this->to = [];
    } else {
        $this->from = $from;
        $this->to = $to;
    }
    Log::info("__construct: $this->from, $this->to"); // doesn't get logged either
}

我尝试记录请求执行的每个步骤。 Intervals 点之前的所有内容都会毫无问题地记录下来,但是当它到达 new Intervals(); 时,它会将我的 VPS 的 CPU 峰值提高到 90%,并且它甚至不记录构造函数中的文本! 我试着等了几分钟,结果还是卡住了!

注意:完全相同的代码在我的 PC 上运行没有问题(两台计算机从同一个 git 存储库运行相同的代码)。

我试图解决的问题:

  • 将 vCPU 数量增加到 2 并且不共享 - 现在它会飙升到 50%,可能是因为它只运行一个单核 - 一半 => 50%
  • 将 RAM 增加到 4GB - 之前和之后完全没有峰值

见鬼是什么问题?为什么用这么简单的构造函数初始化那个类会有问题?

编辑:我反复查看所有 apache2、mysql、Laravel 错误日志,但没有任何相关内容。

【问题讨论】:

  • "__construct: $from, $to" 应该生成警告,因为您不能只在字符串中打印数组(数组不能自动转换为字符串)。不确定这与您遇到的问题有什么关系,但以防万一。
  • 你能分享更多细节吗?如果您从该构造函数中删除行,会发生什么?哪一行导致了问题?
  • 会不会有一个不同的类 Intervals 在某个地方被调用?您是否使用了正确的命名空间?除了记录,您可以尝试dd('test'); 并查看执行是否停止?
  • 其他人怎么知道你班里发生了什么?如果您分享了更多详细信息,其他人可以查看此内容
  • @SenTisso 如果您使用的是自动加载器,那么您的类会在您第一次引用/实例化它时包含在内。类的源文件是否包含类定义本身以外的代码?也可能是自动加载器本身以某种方式崩溃/进入无限循环。

标签: php laravel oop google-compute-engine vps


【解决方案1】:

我简直不敢相信...当 for 循环条件减少时,整个服务器就会变得很糟糕。

我在Intervals 类的一种方法中拥有这部分代码。它应该合并重叠的“从-到”区间,其中$from$to 都是整数数组:

$f = count($from);
$t = count($to);

if ($f !== $t || $f <= 0) return [$from, $to];
array_multisort($from, $to); // sort those arrs, so it can check those arrs gradually

for ($i = 0; $i < $f; $i++) {
    for ($j = $i+1; $j < $f; $j++) { // continue from the next interval

        if ($to[$i] >= $from[$j]) { // if the next interval overlaps the current one
            $to[$i] = max([$to[$i], $to[$j]]);

            // delete the next interval, because it merged with the current one
            array_splice($from, $j, 1);
            array_splice($to, $j, 1);

            $f--; // decrement the array count, because one item got removed
            $j--;
        }
    }
}

为了修复“错误”,我不得不将其修改为未修改(递减)for 循环条件(变量 $f)的版本:

$f = count($from);
$t = count($to);

if ($f !== $t || $f <= 0) return [$from, $to];
array_multisort($from, $to);

for ($i = 0; $i < count($from); $i++) { // count instead of $f
    for ($j = $i+1; $j < count($from); $j++) { // count instead of $f

        if ($to[$i] >= $from[$j]) {
            $to[$i] = max([$to[$i], $to[$j]]);

            array_splice($from, $j, 1);
            array_splice($to, $j, 1);

            /* the $f--; part is no longer here */
            $j--; // <-- weirdly, it has no problems with this part
        }
    }
}

我以这种方式编写了第一部分,因为我想提高性能,通过在每次循环迭代时不调用 count() 并且在数组更改(项目被删除)时减少“静态”计数。

我完全不知道为什么这对我的 VPS 来说是个问题(ubuntu、apache2、PHP 7.4.3)。它只使用 100% 的 CPU (或 100% 的一个内核),没有错误,没有警告,没有 PHP 超时,什么都没有......当我运行我的电脑上完全相同的代码(windows 10、xampp、PHP 7.4.3),一切正常。

另一个奇怪的事情是,执行问题发生在构造函数中的任何内容被执行并且方法本身甚至被调用之前!或者,可能所有这些都秘密首先执行,然后“返回结果”,例如日志、打印等?

或者VPS只是跟我玩愚人节,我不知道......

【讨论】:

  • 在该函数的开头添加 debug_print_backtrace() 以检查它是否被调用以及由代码的哪一部分调用(或者更好的是,如果运行 XDebug,则使用断点)。
  • 该循环向上计数$f$j,并在它们的循环中向下计数两个变量。你有没有试过检查这是否会在未来的任何时候结束?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 1970-01-01
  • 2012-07-11
  • 2013-12-30
  • 1970-01-01
  • 1970-01-01
  • 2019-07-31
相关资源
最近更新 更多