【问题标题】:PHP auto-prepend buggy after out of memory error内存不足错误后PHP自动添加错误
【发布时间】:2014-01-07 06:16:25
【问题描述】:

这可能更适合服务器故障,但我想我会先在这里问。

我们有一个文件,它使用自动前置添加到我们服务器上的每个 PHP 文件,其中包含一个名为 Bootstrap 的类,我们用于自动加载、环境检测等。一切正常。

但是,当对同一服务器上的另一个文件的请求之前(即不到一秒甚至同时)出现“内存不足”错误时,会发生以下三种情况之一:

  1. 我们用于检查if(class_exists('Bootstrap') 的代码(我们在第一次遇到此错误时用于包装类定义)返回true,这意味着尽管这是自动前置文件,但该类已被声明。

  2. 我们从自动前置文件中收到“无法重新声明类 Bootstrap”错误,这意味着 class_exists('Bootstrap') 返回了 false,但它仍然被声明了。

  3. 该文件根本没有前置,导致依赖它的文件出现一次性致命错误。

当然,我们可以尝试修复内存不足问题,因为这些问题似乎会导致其他错误,但由于各种原因,它们在我们的设置中无法修复或很难修复。但这不是重点——在我看来,这是 PHP 中的一个错误,存在某种内存泄漏,导致 auto-prepend 指令出现问题。

这比什么都更令人好奇,因为这种情况很少发生(在我们的高流量服务器上可能每周一次)。但我想知道 - 为什么会发生这种情况,我们可以做些什么来解决它?

我们正在运行 FreeBSD 9.2PHP 5.4.19

编辑:过去几个月我们在尝试解决此问题时注意到的一些事情:

  • 这似乎只发生在我们的安全服务器上。内存不足问题主要出现在我们的安全服务器上(它们通常来自我们自己的员工试图下载过多数据),所以这可能只是巧合,但值得指出

  • 当我们遇到此问题时,get_declared_classes 的转储包含触发错误的页面上未使用的类。例如,$_SERVER 的输出表明此人在 xyz.com 上,但声明的类之一仅在 abc.com 中使用,这通常是内存不足问题的根源。

    李>
  • 所有这一切让我相信 PHP 在出现内存不足错误后没有进行正确的循环结束垃圾收集,这导致 Bootstrap 类完全或部分在内存中如果错误发生后足够快,则请求下一页。我对 PHP 垃圾收集还不够熟悉,无法对此采取实际行动,但我认为这很可能是问题所在。

【问题讨论】:

  • 可能是。听起来像是一个很难追捕的边缘案例。我会使用dtrace 来记录(a)你的 prepend 的编译,(b)GC 正在做什么,以及(c)错误情况。对于 GC 位,您可能需要编写自己的 static probe。另外,如果你有 APC 正在运行,我会为这些诊断禁用它。
  • 谢谢。现在是说服自己这是一个足够大的问题来深入研究解释器和 gc 以修复的困难部分。 :-D
  • 您是否在这些服务器上使用任何类型的操作码缓存?您是使用 php 作为模块还是(快速)cgi?您的 server 内存不足,还是 php 脚本刚刚达到极限?
  • 无缓存。计算机图形图像。据我所知,它只是达到了memory_limit ini 设置。
  • 直截了当 mod_cgi?如果是这样,切换到带有进程隔离的fastcgi,看看是否仍然得到它。

标签: php garbage-collection out-of-memory freebsd


【解决方案1】:

即使class_exists 返回 false,如果存在同名接口,它也永远不会返回 true。但是,您不能声明同名的接口和类。

尝试运行class_exists('Bootstrap') && interface_exists('Bootstrap') 以确保您不会重新声明。

【讨论】:

  • 如果是这样的话,它每次都会发生。我描述的问题会间歇性发生,即使在同一页面上也是如此。我已经离开了那家公司,但无论如何,我们没有使用接口。但是谢谢你的想法
【解决方案2】:

你看过__autoload函数吗?

我相信您可以通过在代码中创建类似的函数来解决此问题:

function __autoload($className)
{
    if (\file_exists($className . '.php')) 
        include_once($className . '.php');
    else 
        eval('class ' . $className . ' { function __call($method, $args) { return false; } }');
}

如果你有一个名为 Bootstrap.php 的文件,其中声明了 Bootstrap 类,PHP 将自动加载文件,否则声明一个可以处理其中任何函数调用的幽灵类,避免任何错误消息。请注意,对于 ghost 功能,我使用了 __call 魔术方法。

【讨论】:

  • Eval 实际上是不好的模式。使用适当的自动加载工具会更好。例如,作曲家中包含的那个。
  • 我同意你@Arziel 的观点,但作曲家实际上没有能力处理不存在的类和它的功能。我调用了 eval() 来构建一个牢不可破的代码。
【解决方案3】:

如果不解决内存不足问题,您可能无法“解决”问题。在不知道您使用的框架的情况下,我将列出我想到的领域列表。

您说“它们通常来自我们自己的员工,试图下载太多数据”。我将从那里开始,因为这可能是最大/最响亮的优化机会,我会想到一些想法。

  • 如果要下载的数据是文件,也许您可​​以使用流将读取分块,使其大小保持不变,这样内存就不会因大下载而被吞噬。

  • 你能做下载排队、限制吗?

  • 如果数据来自数据库,除了优化您的查询外,您还可以限制它们的速率、减小结果集大小并理想地将此类工作负载转移到具有镜像数据的专用环境。

  • 确保您的代码负责任地释放文件指针和数据库连接,将其留给 PHP 拆除,在高流量情况下可能会导致垃圾收集延迟和某种级联效应。

在内存限制方面其他容易实现的目标

  • 您正在运行 php 5.4.19,如果您的软件允许,请考虑更新到更新版本“PHP 5.4 自 2015 年以来未打补丁”,除了 PHP 7 带来了一系列性能改进。

  • 如果您有涉及的客户端应用程序监控它的 xhr 和整体网络活动,请查找过多的轮询和挂起连接。

  • 至于您的自动加载器,根据您的评论“当我们遇到此问题时,get_declared_classes 的转储包含触发错误的页面上未使用的类”,您可能需要检查实现,以确保它不是加载某种捆绑的类缓存,如果您使用的是 composer,dump-autoload 可能会有所帮助。

  • 会话,我已经看到一些应用程序基于 cookie 和会话加载文件,如果您有这样的设置,我会审核该逻辑并确保没有粘性会话加载不需要的资源。

从您的问题中可以清楚地看出您正在运行多租户服务器。如果没有适当的统计数据,很难更具体,但我认为很明显这个问题不是 PHP 问题,因为根据您的描述,它似乎有些孤立。

正确的调试和分析

我建议安装一个 PHP 分析器,即使是很短的时间,new relic 也很不错。您将能够准确地看到正在发生的事情,并拥有数据来解决正确的问题。我认为他们有免费试用版,这应该可以让你找到正确的方向......还有其他人,但他们的名字现在我不知道。

【讨论】:

    猜你喜欢
    • 2012-10-25
    • 2020-02-19
    • 2014-06-01
    • 1970-01-01
    • 2015-09-20
    相关资源
    最近更新 更多