【问题标题】:Impose time limit to popen/fgets in PHP在 PHP 中对 popen/fgets 施加时间限制
【发布时间】:2015-05-14 00:45:16
【问题描述】:

我想对使用 popen 在 PHP 中打开的 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 负载均衡器接收并且是 转发到 EC2 实例。
  3. 答案是文件的最后 30 行
  4. 浏览器在 30 条消息中收到它并保持连接。
  5. 如果 tail 命令发送一个新行,则返回,否则 fgets 等待未定义的时间,直到从 tail 命令返回新行。
  6. AWS 负载均衡器在网络不活动 60 秒后(60 秒内没有新行)关闭与浏览器的连接。与 EC2 实例的连接未关闭。
  7. 浏览器检测到连接已关闭并打开一个新连接,过程返回步骤1。

正如这个步骤所描述的,AWS 负载均衡器和 EC2 实例之间的连接永远不会关闭,几个小时/天后,有成百上千个 tail 和 httpd 进程在运行,服务器开始没有响应。

当然,这似乎是一个 AWS 负载均衡器错误,但我不想启动一个流程来获得亚马逊的关注并等待修复。

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

我认为 PHP 不会停止脚本,因为 PHP 被“阻止”等待 fgets 完成。

我知道 AWS 负载均衡器的时间限制是可编辑的,但我想保持默认值,即使更高的限制也无法解决问题。

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

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

【问题讨论】:

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


    【解决方案1】:

    用 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);
    

    【讨论】:

    • 这是我在不使用任何扩展的情况下能做的最好的事情。这不是很好。如果 PHP 或 Apache 崩溃,您仍然可以留下孤立进程。此外,您可能希望将SIGKILL 用于proc_terminate
    【解决方案2】:

    我没有使用进程文件指针,而是采用了我的“多任务处理”方法。我使用这段代码来生成其他“进程”,这是一种多任务作弊。

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

    您可能需要调整 stream 和 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');
    

    【讨论】:

      猜你喜欢
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-23
      相关资源
      最近更新 更多