【问题标题】:PHP Process Execution TimeoutPHP 进程执行超时
【发布时间】:2011-03-15 09:41:56
【问题描述】:

我有以下代码:

/**
 * Executes a program and waits for it to finish, taking pipes into account.
 * @param string $cmd Command line to execute, including any arguments.
 * @param string $input Data for standard input.
 * @param integer $timeout How much to wait from program in msecs (-1 to wait indefinitely).
 * @return array Array of "stdout", "stderr" and "return".
 */
function execute($cmd,$stdin=null,$timeout=-1){
    $proc=proc_open(
        $cmd,
        array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
        $pipes=null
    );
    fwrite($pipes[0],$stdin);                  fclose($pipes[0]);
    $stdout=stream_get_contents($pipes[1]);    fclose($pipes[1]);
    $stderr=stream_get_contents($pipes[2]);    fclose($pipes[2]);
    $return=proc_close($proc);
    return array(
        'stdout' => $stdout,
        'stderr' => $stderr,
        'return' => $return
    );
}

它有两个“问题”。

  • 代码是同步的;它会冻结,直到目标进程关闭。
  • 到目前为止,如果不发出不同类型的命令(例如 Linux 上的 $cmd > /dev/null & 和 Windows 上的 start /B $cmd),我就无法将其从“冻结”状态中解脱出来

我完全不介意“冻结”。我只需要实现那个超时。

注意:解决方案跨平台兼容很重要。 $cmd 不必更改也很重要 - 我正在运行一些复杂的命令,我担心可能会出现一些问题,但是,这取决于修复的类型 - 我很高兴听到这些,只是我更喜欢不同的选择。

我找到了一些可能有帮助的资源:

【问题讨论】:

    标签: php process timeout proc-open


    【解决方案1】:

    代码有一些错误。

    这确实有效:

    function execute($cmd, $stdin = null, $timeout = -1)
    {
        $proc=proc_open(
            $cmd,
            array(array('pipe','r'), array('pipe','w'), array('pipe','w')),
            $pipes
        );
        var_dump($pipes);
        if (isset($stdin))
        {
            fwrite($pipes[0],$stdin);
        }
        fclose($pipes[0]);
    
        stream_set_timeout($pipes[1], 0);
        stream_set_timeout($pipes[2], 0);
    
        $stdout = '';
    
        $start = microtime();
    
        while ($data = fread($pipes[1], 4096))
        {
            $meta = stream_get_meta_data($pipes[1]);
            if (microtime()-$start>$timeout) break;
            if ($meta['timed_out']) continue;
            $stdout .= $data;
        }
    
        $stdout .= stream_get_contents($pipes[1]);
        $stderr = stream_get_contents($pipes[2]);
        $return = proc_close($proc);
    
        return array(
            'stdout' => $stdout,
            'stderr' => $stderr,
            'return' => $return
        );
    }
    

    【讨论】:

    • 如果我运行execute('php test.php', 3),并且test.php休眠30秒没有输出,那么这个函数会无限期阻塞。
    【解决方案2】:

    而不是stream_get_contents,您可以考虑使用fread 来更精细地控制您的代码正在做什么。结合stream_set_timeout 可能会给您想要的东西。

    我将一些东西放在一起,以展示我认为可能有效的方法 - 此代码完全未经测试,不提供任何保证,但可能会将您引向正确的方向。 ;)

    function execute($cmd,$stdin=null,$timeout=-1){
        $proc=proc_open(
            $cmd,
            array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
            $pipes=null
        );
        fwrite($pipes[0],$stdin);                  fclose($pipes[0]);
    
        stream_set_timeout($pipes[1], 0);
        stream_set_timeout($pipes[2], 0);
    
        $stdout = '';
    
        $start = microtime();
    
        while ($data = fread($pipes[1], 4096))
        {
            $meta = stream_get_meta_data($pipes[1]);
            if (microtime()-$start>$timeout) break;
            if ($meta['timed_out']) continue;
            $stdout .= $data;
        }
    
        $return = proc_close($proc);
        $stdout .= stream_get_contents($pipes[1]);
        $stderr = stream_get_contents($pipes[2]);
    
        return array(
            'stdout' => $stdout,
            'stderr' => $stderr,
            'return' => $return
        );
    }
    

    【讨论】:

      【解决方案3】:

      这似乎对我有用:

      public function toPDF() {
          $doc = $this->getDocument();
      
          $descriptor = [
              ['pipe','r'],
              ['pipe','w'],
              ['file','/dev/null','w'], // STDERR
          ];
          $proc = proc_open('/usr/local/project/scripts/dompdf_cli.php',$descriptor,$pipes,sys_get_temp_dir());
          fwrite($pipes[0],"$doc[paper]\n$doc[html]");
          fclose($pipes[0]);
      
          $timeout = 30;
      
          stream_set_blocking($pipes[1], false);
      
          $pdf = '';
      
          $now = microtime(true);
      
          try {
              do {
                  $elapsed = microtime(true) - $now;
      
                  if($elapsed > $timeout) {
                      throw new \Exception("PDF generation timed out after $timeout seconds");
                  }
                  $data = fread($pipes[1], 4096);
                  if($data === false) {
                      throw new \Exception("Read failed");
                  }
                  if(strlen($data) === 0) {
                      usleep(50);
                      continue;
                  }
                  $pdf .= $data;
              } while(!feof($pipes[1]));
      
              fclose($pipes[1]);
              $ret = proc_close($proc);
          } catch(\Exception $ex) {
              fclose($pipes[1]);
              proc_terminate($proc); // proc_close tends to hang if the process is timing out
              throw $ex;
          } 
      
      
          if($ret !== 0) {
              throw new \Exception("dompdf_cli returned non-zero exit status: $ret");
          }
      
          // dump('returning pdf');
          return $pdf;
      }
      

      我不确定stream_set_timeout 的用途是什么——它只是设置每次读取超时,但如果你想限制总时间,你只需将流设置为非阻塞模式并且然后计算需要多长时间。

      【讨论】:

        猜你喜欢
        • 2012-11-18
        • 2011-02-06
        • 1970-01-01
        • 2011-08-06
        • 2010-09-07
        • 2017-05-01
        • 1970-01-01
        • 2014-02-09
        • 1970-01-01
        相关资源
        最近更新 更多