Php popen / fgets time limit

I want to impose a time limit on a process read using fgets opened by popen in PHP.

I have the following code:

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

      

I tried without success:

set_time_limit(60);
ignore_user_abort(false);

      

The process is as follows:

  • The browser sends a GET request awaiting a response on the server side HTML5 event format.
  • The request is received by the AWS load balancer and is forwarded to the EC2 instances.
  • The answer is the last 30 lines of the file
  • The browser receives it in 30 messages and the connection is maintained.
  • If the tail command sends a newline, it returns else fgets wait undefined time until a newline is returned from the tail command.
  • AWS load balancing after 60 seconds of network inactivity (no new lines in 60 seconds) closes the browser connection. The connection to the EC2 instance is not closed.
  • The browser detects that the connection is closed and it opens a new connection, the process goes back to step 1.

As these steps describe, the connection between AWS Load Balancer and EC2 instance never closes, after a few hours / days, hundreds and hundreds of tail and httpd processes are running and the server is not responding.

Of course this is a bug of AWS Load Balancer, but I don't want to start the process to get attention from Amazon and wait for a fix.

My workaround is a sudo kill tail to kill the process before the server becomes unstable.

I think PHP is not stopping the script because PHP is "blocked" waiting for fgets to finish.

I know the AWS Load Balancer limit is editable, but I want to keep the default, even a higher limit will not fix the problem.

I don't know if I need to change the question of how to execute a process in linux with a time limit / timeout ?.

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

+3


source to share


2 answers


Tested with 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);

      

0


source


Instead of using a process file pointer, I went with my "multitasking" approach. I am using this code to spawn other "processes". A type of multitasking cheat.

I'm calling the Script, hang.php, who just hangs for 90 seconds: sleep(90)

.

You might want to adjust the stream and stream_select timeouts.

Create stream (s)

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";
} 

      

Additional scripts can be started by adding a stream: $sockets[$id++] = $stream;


Below will be placed something in the array $result[$id]

.



Monitoring streams:

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

      

...


When I want to kill a process I use system('ps auxww')

to get the pid and kill it withsystem("kill $pid")

kill.php

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

echo system('ps auxww');

      

0


source







All Articles