在PHP中对popen / fgets施加时间限制

时间:2015-05-14 00:45:17

标签: php amazon-web-services tail time-limiting

我希望使用PHP中的popen打开的fgets对进程读取施加时间限制。

我有下一个代码:

$handle = popen("tail -F -n 30 /tmp/pushlog.txt 2>&1", "r");
while(!feof($handle)) {
    $buffer = fgets($handle);
    echo "data: ".$buffer."\n";
    @ob_flush();
    flush();
}
pclose($handle);

我尝试没有成功:

set_time_limit(60);
ignore_user_abort(false);

流程如下:

  1. 浏览器发送GET请求,等待HTML5服务器端的应答 事件格式。
  2. AWS Load Balancer收到请求,并且是 转发给EC2实例。
  3. 答案是文件的最后30行
  4. 浏览器会收到30封邮件,并且连接仍然存在。
  5. 如果tail命令发送一个新行,则返回其他fgets等待未定义的时间,直到从tail命令返回新行。
  6. 网络不活动60秒后的AWS Load Balancer(60秒内没有新行)关闭与浏览器的连接。与EC2实例的连接未关闭。
  7. 浏览器检测到连接已关闭并打开新连接,该过程将返回步骤1.
  8. 根据此步骤描述,AWS Load Balancer和EC2实例之间的连接永远不会关闭,几小时/几天后,有数百个尾部和httpd进程在运行且服务器开始没有响应。

    当然它似乎是一个AWS Load Balancer错误,但我不想开始一个流程来吸引亚马逊的注意并等待修复。

    我的临时解决方案是在服务器变得不稳定之前执行sudo kill tail来终止进程。

    我认为PHP并没有停止脚本,因为PHP已被阻止"等待fgets完成。

    我知道AWS Load Balancer的时间限制是可编辑的,但我希望保持默认值,即使更高的限制也无法解决问题。

    我不知道是否需要将问题更改为如何在时间限制/超时的情况下在linux中执行进程?

    PHP 5.5.22 / Apache 2.4 / Linux内核3.14.35-28.38.amzn1.x86_64

2 个答案:

答案 0 :(得分:0)

使用PHP 5.5.20进行测试:

//Change configuration.
set_time_limit(0);
ignore_user_abort(true);

//Open pipe & set non-blocking mode.
$descriptors  = array(0 => array('file', '/dev/null', 'r'),
                      1 => array('pipe', 'w'),
                      2 => array('file', '/dev/null', 'w'));
$process      = proc_open('exec tail -F -n 30 /tmp/pushlog.txt 2>&1',
                                $descriptors, $pipes, NULL, NULL) or exit;
$stream       = $pipes[1];
stream_set_blocking($stream, 0);

//Call stream_select with a 10 second timeout.
$read = array($stream); $write = NULL; $except = NULL;
while (!feof($stream) && !connection_aborted()
        && stream_select($read, $write, $except, 10)) {

    //Print out all the lines we can.
    while (($buffer = fgets($stream)) !== FALSE) {
        echo 'data: ' . $buffer . "\n";
        @ob_flush();
        flush();
    }

}

//Clean up.
fclose($stream);
$status = proc_get_status($process);
if ($status !== FALSE && $status['running'] === TRUE)
    proc_terminate($process);
proc_close($process);

答案 1 :(得分:0)

我没有使用过程文件指针,而是使用了我的"多任务处理"做法。我使用这段代码来产生其他"进程"有点多任务作弊。

我调用了一个脚本hang.php,它只挂了90秒:sleep(90)

您可能需要调整流和stream_select超时。

创建信息流

header('Content-Type: text/plain; charset=utf-8');
$timeout = 20; 
$result = array(); 
$sockets = array(); 
$buffer_size = 8192;
$id = 0;
$stream = stream_socket_client("ispeedlink.com:80", $errno,$errstr, $timeout,
    STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT); 
if ($stream) {
  $sockets[$id++] = $stream;  // supports multiple sockets
  $http = "GET /testbed/hang.php HTTP/1.0\r\nHost: ispeedlink.com\r\n\r\n"; 
  fwrite($stream, $http);
} 
else { 
  echo "$id Failed\n";
} 

可以通过添加流来运行其他脚本:$sockets[$id++] = $stream;

下面会将所有内容读入$result[$id]数组。

监控流:

while (count($sockets)) {
  $read = $sockets; 
  stream_select($read, $write = NULL, $except = NULL, $timeout); 
  if (count($read)) {
    foreach ($read as $r) { 
      $id = array_search($r, $sockets); 
      $data = fread($r, $buffer_size); 
      if (strlen($data) == 0) { // either reads data or EOF
        echo "$id Closed: " . date('h:i:s') . "\n\n\n";
        fclose($r); 
        unset($sockets[$id]);
      } 
      else {
        $result[$id] .= $data; 
      }
    }
  }
  else { 
    echo 'Timeout: ' . date('h:i:s') . "\n\n\n";
    break;
  }
}
echo system('ps auxww');

当我想杀死一个进程时,我使用system('ps auxww')获取pid并使用system("kill $pid")

将其杀死

<强> kill.php

header('Content-Type: text/plain; charset=utf-8');
//system('kill 220613');

echo system('ps auxww');