【问题标题】:php sleep function odd behaviourphp睡眠功能奇怪的行为
【发布时间】:2016-01-05 11:36:51
【问题描述】:

我们继承了一个平台,该平台有一个 crobjob,它每分钟使用不同的参数 (curl -s -o --url https://localhost/myscript.php?option=XYZ -k) 将本地 php 脚本卷曲 3 次。该脚本运行大约 1 分钟,并且它可能具有相同选项的多个实例会重叠一段时间。脚本根据给定的选项记录在不同的文件中,每个日志都以脚本启动时的时间戳开始,因此它充当实例标识符。

脚本有这个骨架:

<?php

$option=XYZ;
$scriptId = time();
$file = "log_$option.txt";

file_put_contents($file,"\n$scriptId: Start\n",FILE_APPEND);
session_start();

$expires = time()+60;
file_put_contents($file,"\n$scriptId: Expires at $expires\n",FILE_APPEND);

while(time()<$expires){

    file_put_contents($file,"\n$scriptId: Not expired at ".time()."\n",FILE_APPEND);

    switch($option){
        case X:
            do_db_stuff();
            break;
        ...
    }

    file_put_contents($file,"\n$scriptId: Will sleep at ".time()."\n",FILE_APPEND);
    sleep(13);
    file_put_contents($file,"\n$scriptId: Woke up at ".time()."\n",FILE_APPEND);
}

file_put_contents($file,"\n$scriptId: Finished at ".time()."\n",FILE_APPEND);

通常,此脚本运行良好(即使它们在实例 A 最后一次休眠并且实例 B 启动时重叠)但有时我们可以通过日志确认两个问题:

  • 有时它的睡眠时间少于 13 秒(a 可变的时间量,始终小于 13);
  • 有时脚本会停止(在“Will sleep”之后不再记录日志,我们可以验证没有执行任何数据库操作)。 [在编辑 2 中对此进行更新]

我们已经调查了可能的原因,但找不到任何原因:

  • php max_execution_time 设置为 240 秒,脚本从不 耗时超过一分半钟;
  • sleep 文档说它是每个会话,但 curl 没有使用 cookie,所以它应该是每个实例中的不同会话(而且如果它使用相同的会话,它总是会阻塞,因为我们总是执行三个脚本实例,它没有t);
  • 托管技术团队表示服务器中也没有错误 错误日志也不在这些问题的时间戳中的php错误日志中 发生。

我无法随意重现这些问题,但它们每天至少发生一次。 我想知道什么会干扰睡眠行为?如何检测或修复它?

附加信息:

  • linux系统
  • mysql 5.5
  • 阿帕奇
  • php 5.3
  • php max_execution_time 设置为 240

编辑 1: 澄清一下:实际上我们有 3 个选项,所以它写入 3 个日志文件,每个选项一个。在任何给定时间,每个选项最多可以运行两个实例(同一选项的每个实例重叠一小段时间)。

Edit2:根据@Jan 的建议,我将日志添加到睡眠功能结果中。脚本已经用该日志停止了一次:

[2016-01-05, 13:11:01] Will sleep at 2016-01-05, 13:11:29
[2016-01-05, 13:11:01] Woke up at 2016-01-05, 13:11:37 with sleep return 5
[2016-01-05, 13:11:01] Not expired at 2016-01-05, 13:11:37
[2016-01-05, 13:11:01] Will sleep at 2016-01-05, 13:11:37
[2016-01-05, 13:11:01] Woke up at 2016-01-05, 13:11:38 with sleep return 13
... no more log from instance [2016-01-05, 13:11:01] ...
[2016-01-05, 13:12:01] Start

根据sleep 文档:

如果调用被信号中断,sleep() 返回一个非零值。在 Windows 上,此值将始终为 192(Windows API 中的 WAIT_IO_COMPLETION 常量的值)。在其他平台上,返回值将是剩余的休眠秒数。

因此,根据文档和日志,sleep 似乎由于中断而被缩短。

我怎么知道是什么中断导致了这个(pcntl_signal?),它是从哪里来的,有什么办法可以避免它?

Edit3:我添加了代码来处理带有 pcntl_signal 的信号(尝试从信号 1 注册到 255)并记录它们,问题仍然存在,但日志仍然是空的。

【问题讨论】:

  • 只是一个提示,并不是真正的解决方案:我会使用唯一标识符 (uniqid()) 而不是 $option 来查看它是否确实只有一个文件.此外,尝试记录 sleep() 的返回值 - 一旦它捕获到信号,它是肯定的,如果发生错误,它是 false。
  • 感谢您的建议,一定会记录sleep的返回值

标签: php apache curl cron sleep


【解决方案1】:

您可以使用 pcntl_signal 定义信号处理程序。

使用这些处理程序,您可以在发生中断时进行记录。但是 AFAIK 你无法检测到它来自哪里。

您还可以将 pcntl_alarm 用于延迟作业。 Check PHP Manual - PCNTL Alarm

【讨论】:

    【解决方案2】:

    鉴于您的堆栈,中断信号可能来自 apache。根据 Apache 在您的堆栈上与 PHP 通信的方式,Apache 配置中有几个超时选项可以推断脚本执行。

    如果您的 cron 在 localhost 上调用 curl,也许它可以直接调用 PHP 文件,从而避免使用 apache 进程来保持请求处于活动状态?您必须使用 max_execution_time 值编辑专用 CLI php.ini 文件。

    【讨论】:

      猜你喜欢
      • 2021-04-12
      • 1970-01-01
      • 1970-01-01
      • 2015-02-16
      • 1970-01-01
      • 1970-01-01
      • 2021-12-27
      • 1970-01-01
      相关资源
      最近更新 更多