【问题标题】:PHP set timeout for script with system call, set_time_limit not workingPHP使用系统调用为脚本设置超时,set_time_limit不起作用
【发布时间】:2010-04-08 22:33:25
【问题描述】:

我有一个命令行 PHP 脚本,它使用带有 foreach 的数组的每个成员来运行 wget 请求。这个 wget 请求有时可能需要很长时间,所以我希望能够设置一个超时来杀死脚本,例如,如果它超过 15 秒。我禁用了 PHP 安全模式并在脚本的早期尝试了 set_time_limit(15),但它会无限期地继续。 更新:感谢 Dor 指出这是因为 set_time_limit() 不尊重 system() 调用。

所以我试图找到其他方法在执行 15 秒后终止脚本。但是,我不确定是否可以在同时处于 wget 请求中间时检查脚本的运行时间(do while 循环不起作用)。也许 fork 一个带有计时器的进程并设置它在设定的时间后杀死父进程?

感谢您的任何提示!

更新: 下面是我的相关代码。 $url 是从命令行传递的,是一个包含多个 URL 的数组(很抱歉最初没有发布):

foreach( $url as $key => $value){
    $wget = "wget -r -H -nd -l 999 $value";
    system($wget);
    }

【问题讨论】:

    标签: php windows linux command-line


    【解决方案1】:

    除了set_time_limit() 之外,尝试使用 wget 命令行参数--timeout。

    记住set_time_limit(15) 从零重新启动超时计数器所以不要在循环中调用它(为了你的目的)

    来自man wget:

    --timeout=秒

    设置网络 超时秒秒。这是 相当于指定 --dns-timeout、--connect-timeout 和 --read-timeout,都在同一时间。

    与网络交互时, wget 可以检查超时和中止 如果操作时间过长。 这样可以防止挂起等异常情况 读取和无限连接。唯一的 默认情况下启用的超时是 900 秒读取超时。设置一个 超时为 0 完全禁用它。 除非你知道自己在做什么,否则 最好不要更改默认值 超时设置。

    所有与超时相关的选项都接受 十进制值,以及亚秒 价值观。例如,0.1 秒是 合法(虽然不明智)的选择 暂停。亚秒超时是 用于检查服务器响应 次或用于测试网络延迟。

    编辑:好的。我明白你现在在做什么。你可能应该做的是使用proc_open而不是system,并使用time()函数检查时间,如果wget tskes太长则调用proc_terminate。

    【讨论】:

    • 感谢您的帮助,但不幸的是,这对我的目的不起作用。我很抱歉没有发布相关代码,这会使这一点更清楚(见上面更新的代码问题)。所以 wget 的 timeout 参数只是为每个 wget 请求设置,而不是整个脚本。因此,当我将 5 个 URL 传递给脚本时,每个 URL 的超时时间为 20,总共至少需要 100 秒,通常更多是因为递归选项。 Dor 强调了为什么 set_time_limit() 不起作用。
    【解决方案2】:

    您可以结合使用“--timeout”和 time()。首先计算您总共有多少时间,然后在脚本运行时降低 --timeout。

    例如:

    $endtime = time()+15;
    foreach( $url as $key => $value){
      $timeleft = $endtime - time();
      if($timeleft > 0) {
        $wget = "wget -t 1 --timeout $timeleft $otherwgetflags $value";
        print "running $wget<br>";
        system($wget);
      } else {
        print("timed out!");
        exit(0);
      }
    }
    

    注意:如果不使用-t,wget会尝试20次,每次等待--timeout秒。

    这是一些使用 proc_open/proc_terminate 的示例代码(@Josh 的建议):

    $descriptorspec = array(
       0 => array("pipe", "r"),
       1 => array("pipe", "w"),
       2 => array("pipe", "w")
    );
    $pipes = array();
    
    $endtime = time()+15;
    foreach( $url as $key => $value){
      $wget = "wget $otherwgetflags $value";
      print "running $wget\n";
      $process = proc_open($wget, $descriptorspec, $pipes);
      if (is_resource($process)) {
        do {
          $timeleft = $endtime - time();
          $read = array($pipes[1]);
          stream_select($read, $write = NULL, $exeptions = NULL, $timeleft, NULL);
          if(!empty($read)) {
            $stdout = fread($pipes[1], 8192);
            print("wget said--$stdout--\n");
          }
        } while(!feof($pipes[1]) && $timeleft > 0);
        if($timeleft <= 0) {
          print("timed out\n");
          proc_terminate($process);
          exit(0);
        }
      } else {
        print("proc_open failed\n");
      }
    }
    

    【讨论】:

    • proc_open/proc_terminate 对我来说效果很好。感谢所有贡献的人! (希望我也可以给 Josh 点赞,而是给了 +1)。
    • @tehalive:很好的解决方案!不要担心不“给我信用”,我的答案已经 +5,你的 +1 使它更接近另一个“好答案”徽章 xD
    • @tehalive:请注意,如果您将我的代码用于 proc_open/proc_terminate,请使用 -q wget 标志,否则它将尝试写入 stderr。
    【解决方案3】:

    您可以将 wget 程序的输出定向到一个文件,然后该文件立即返回。
    另见:

    注意:set_time_limit() 函数 和配置指令 max_execution_time 只影响 脚本本身的执行时间。 花在活动上的任何时间 发生在执行之外 脚本,例如系统调用使用 system(),流操作,数据库 查询等不包括在内时 确定的最长时间 脚本一直在运行。这不是 在测量的 Windows 上为 true 时间是真实的。

    来源:set_time_limit()@php.net

    【讨论】:

    • +1。他还需要限制 wget 的执行时间,看我的回答
    • @Josh:我认为他的意思是 PHP 脚本:“所以我希望能够设置一个超时来杀死脚本,例如,如果它超过 15 秒”。但是注意到 wget 超时也是值得赞赏和有帮助的:)
    • 是的,但正如你所说,set_time_limit 对 wget 等外部命令没有影响——所以要限制脚本的总时间(包括 wget,因为它是从脚本调用的),wget 也必须是有限的。无论如何,这就是我对这个问题的理解......
    【解决方案4】:

    我认为最好的方法是处理命令本身的杀戮,至少在 Linux(Ubuntu 17.10)上。没有其他方法对我有用......来自另一个答案的proc_terminate 有效,但不会给出任何输出。

    在下面的示例中,我添加了一个魔术字符串来检查您的进程是否仍在运行。如果运行时间超过$sec,它会立即终止进程,并留下一条注释“超时!”。

    像这样,我确信命令不会超过超时,并且我会收到我需要的所有 stdout/stderr。

    $wget = "wget -r -H -nd -l 999 $value"; // your command
    
    $sec = 5; // timeout in seconds
    $cmd = $wget . " & ii=0 && while [ \"2\" -eq \"`ps -p $! | wc -l \" ]; do ii=$((ii+1)); if [ \$ii -gt ".($sec)."0 ]; then echo 'Timeout!';kill $!; break;fi; sleep 0.1; done"
    system($cmd); // system call
    

    【讨论】:

      猜你喜欢
      • 2015-05-02
      • 2017-02-18
      • 2021-11-07
      • 2012-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-17
      • 1970-01-01
      相关资源
      最近更新 更多