【问题标题】:Best practice to find source of PHP script termination查找 PHP 脚本终止源的最佳实践
【发布时间】:2010-11-24 23:37:41
【问题描述】:

我有一个 PHP 脚本,它从数据库中抓取一大块数据,对其进行处理,然后查看是否还有更多数据。这个进程无限期地运行,我在一个服务器上一次运行其中的几个。

它看起来像:

<?php
    while($shouldStillRun)
    {
       // do stuff
    }
    logThatWeExitedLoop();
?>

问题是,一段时间后,某些原因导致进程停止运行,我无法对其进行调试并确定原因。

这是我目前用来获取信息的内容:

  • error_log - 记录所有错误,但错误日志中不显示任何错误。
  • register_shutdown_function - 注册了一个自定义关闭函数。这确实被调用了,所以我知道该进程没有被服务器杀死,它被允许完成。 (或者至少我认为这是被调用的情况?)
  • debug_backtrace - 在我的自定义关闭函数中记录了 debug_backtrace()。这仅显示一个呼叫,这是我的自定义关闭功能。
  • 如果到达脚本结尾记录 - 在循环之外,我有一个函数可以记录脚本退出循环(因此通常会到达源文件的结尾)。当脚本随机终止时,它不会记录这个,所以无论什么杀死它,都会在它正在处理的过程中杀死它。

您还建议使用哪些其他调试方法来查找罪魁祸首?

注意:我应该补充一点,这不是 max_execution_time 的问题,这些脚本已禁用它。被杀前的时间不一致。它可以运行 10 秒或 12 小时后死亡。


更新/解决方案:谢谢大家的建议。通过记录输出,我发现当 MySql 查询失败时,脚本被设置为 die()。哦。更新它以记录 mysql 错误,然后终止。让它现在像魅力一样工作!

【问题讨论】:

  • php.net/manual/en/features.connection-handling.php 说“当计时器到期时,脚本将被中止,并且与上述客户端断开连接情况一样,如果已注册关闭函数,它将被调用。”所以你的陈述“这确实被调用了,所以我知道该进程没有被服务器杀死”,可能是错误的。
  • 正如您所指出的,debug_backtrace 在关闭函数中是无用的,因为关闭函数有一个独立的调用堆栈,与触发它的任何原因无关。

标签: php apache debugging


【解决方案1】:

我会记录您的脚本的内存使用情况。也许它获得了太多的内存,达到了内存限制并死了?

【讨论】:

    【解决方案2】:

    请记住,PHP 在 ini 文件中有一个变量,表示脚本应该运行多长时间。 max-execution-time

    确保您没有超出此范围,或使用 set_time_limit() 来增加执行时间。这个程序是通过 Web 服务器还是通过 cli 运行的?

    补充:我对 PHP 的糟糕体验。浏览我今年早些时候写的一些背景脚本。抱歉,PHP 是一种糟糕的脚本语言,可以长时间做任何事情。我看到较新的 PHP(我们还没有升级到)添加了强制 GC 运行的功能。我遇到的问题是使用过多的内存,因为 GC 几乎从不运行来清理自己。如果你使用递归引用自身的东西,它们也永远不会被释放。

    创建一个包含 100,000 个元素的数组会产生内存,但随后将该数组设置为一个空数组或将其全部拼接,不会立即释放它,也不会将其标记为未使用(也就是创建一个新的 100,000 个元素的数组会增加记忆)。

    我个人的解决方案是编写一个永久运行的 perl 脚本,然后 system("php my_php.php");需要时,让口译员完全解放。我目前支持 5.1.6,这可能会在 5.3+ 中得到修复,或者至少,现在他们有 GC 命令可以用来强制 GC 进行清理。

    简单脚本

    #!/usr/bin/perl -w 使用严格; 而(1){ 如果(系统(“php /to/php/script.php”)!= 0){ 睡眠(30); } }

    然后在你的 php 脚本中

    【讨论】:

    • 见我上面的注释 :) - 最大执行时间不是问题。该脚本由另一个启动脚本启动。该脚本通过以下方式启动每个进程: exec('php /script/to/run.php > /dev/null 2>&1 & echo $!', $output); (我得到了进程 ID,所以我可以跟踪它以后是否还活着)
    • 您是否尝试过将脚本的输出记录到日志文件中?
    • exec("php /script/to/run.php >/to/log/file.txt 2>&1 & echo $!")
    • 好主意,我会试试的。该脚本不输出任何内容,但不是检查某些测试的坏方法。
    【解决方案3】:

    我会将函数的状态记录到每个循环中几个不同位置的文件中。

    您可以使用var_export 形式将大多数变量的内容作为字符串获取。

    您可以将其记录到某个文件中,并密切关注它。日志结束前函数的最新状态应该可以提供一些线索。

    【讨论】:

      【解决方案4】:

      听起来无论发生什么都不是标准的 php 错误。您应该能够使用 try...catch 语句抛出您自己的错误,然后记录该语句。除此之外,我没有更多详细信息,因为我的手机远离电脑。

      【讨论】:

      • try {something();}catch(Exception $e){trigger_error('your error text :'.$e,E_USER_WARNING);}。不过,代码必须抛出异常才能使 try/catch 起作用。
      【解决方案5】:

      我以前在我们的一个工作项目中遇到过这种情况。我们有一个类似的设置——一个 PHP 脚本检查数据库是否有要完成的任务(例如发送电子邮件、更新记录、处理一些数据)。 PHP脚本里面有一个while循环,设置为

      while(true) {
          //do something
      }
      

      一段时间后,脚本也会以某种方式被杀死。我已经尝试过这里所说的大部分内容,例如设置 max_execution_time、使用 var_export 记录所有输出、放置 try_catch、制作脚本输出(php ... > output.txt)等,但我们从未能够做到找出问题所在。

      我认为 PHP 本身并不是为了执行后台任务而构建的。我知道它没有回答您的问题(如何调试它),但我们的工作方式是我们使用 cronjob 每 5 分钟调用一次 PHP 文件。这类似于 Jeremy 对使用 perl 脚本的回答——它确保解释器在执行完成后是空闲的。

      【讨论】:

        【解决方案6】:

        如果这是在 Linux 上,请尝试查看系统日志 - 进程可能会被 OOM(内存不足)杀手杀死(不太可能,如果发生这种情况,您还会看到其他问题),或者分段错误(某些版本的 PHP 不喜欢某些版本的扩展,导致奇怪的崩溃)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-10-12
          • 1970-01-01
          • 1970-01-01
          • 2021-01-24
          • 2021-03-15
          • 1970-01-01
          • 2014-10-02
          相关资源
          最近更新 更多