在PHP中对popen / fgets施加时间限制
I want impose a time limit to a process reading using fgets opened by popen in PHP.
I have the next code:
$handle = popen("tail -F -n 30 /tmp/pushlog.txt 2>&1", "r");
while(!feof($handle)) {
$buffer = fgets($handle);
echo "data: ".$buffer."
";
@ob_flush();
flush();
}
pclose($handle);
I tried without success:
set_time_limit(60);
ignore_user_abort(false);
The process is as follow:
- The browser send a GET request waiting for a Answer in HTML5 Server side events format.
- The request is received by AWS Load Balancer and is forwarded to EC2 instances.
- The answer is the last 30 lines of the file
- The browser receive it in 30 messages and the connection is persisted.
- If tail command sends a new line it is returned else fgets wait undefined time until new line is returned from tail command.
- AWS Load Balancer after 60 seconds of network inactivity (No new lines in 60 seconds) closes the connection to the browser. The connection to EC2 instance is not closed.
- The browser detect that the connection is closed and it opens a new connection, the process go back to step 1.
AS this steps describe, the connection between AWS Load Balancer and EC2 instance is never closed, after a few hours/days there is hundreds and hundreds of tail and httpd process running and the server start not answering.
Of course it appear to be a AWS Load Balancer bug, but I don't want start a process to gain the attention from Amazon and wait for a fix.
My temporary solution is do a sudo kill tail to kill the process before the server becomes unstable.
I think PHP doesn't stop the script because PHP is "blocked" waiting for fgets to finish.
I know that the time limit of AWS Load Balancer is editable, but I want keep in the default value, even a higher limit is not going to fix the problem.
I don't know if I need change the question to 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
我想在PHP中使用popen打开的fgets对进程读取施加时间限制。 p>
我有下一个代码: p>
$ handle = popen(“tail -F -n 30 /tmp/pushlog.txt 2>& 1”, “r”);
while(!feof($ handle)){
$ buffer = fgets($ handle);
echo“data:”。$ buffer。“
”;
@ob_flush();
flush();
}
clclose($ handle);
code> pre>
我试过没有成功: p>
set_time_limit(60);
ignore_user_abort(false);
code> pre>
过程如下: p>
浏览器发送GET请求,等待HTML5 Server side
events格式的答案。 li>
- 请求由AWS Load Balancer收到,并且已转发给EC2实例。 li>
- 答案是文件的最后30行 li>
- 浏览器在30条消息中接收它并且连接是持久的。 li>
- 如果tail命令发送新行,则返回其他fgets等待未定义时间,直到从tail命令返回新行。 / li>
- 网络不活动60秒后,AWS Load Balancer(60秒内没有新行)关闭与浏览器的连接。 与EC2实例的连接未关闭。 li>
- 浏览器检测到连接已关闭并打开新连接,该过程将返回步骤1. li>
ol >
根据此步骤描述,AWS Load Balancer和EC2实例之间的连接永远不会关闭,几小时/几天后,有数百个尾部和httpd进程正在运行且服务器开始无法应答。 p>
当然它似乎是一个AWS Load Balancer错误,但我不想启动一个流程来吸引亚马逊的注意并等待修复。 p>
我的临时解决方案是在服务器变得不稳定之前执行sudo kill tail来终止进程。 p>
我认为PHP不会停止脚本,因为PHP被“阻止” 等待fgets完成。 p>
我知道AWS Load Balancer的时间限制是可编辑的,但我希望保持默认值,即使更高的限制也无法解决问题 。 p>
我不知道是否需要将问题改为如何执行 Linux中的进程是否有时间限制/超时?。 p>
PHP 5.5.22 / Apache 2.4 / Linux内核3.14.35-28.38.amzn1.x86_64 p>
DIV>
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 . "
";
@ob_flush();
flush();
}
}
//Clean up.
fclose($stream);
$status = proc_get_status($process);
if ($status !== FALSE && $status['running'] === TRUE)
proc_terminate($process);
proc_close($process);
Rather than using a process file pointer, I went with my "multitasking" approach. I use this code to spawn other "processes" Kind of a multitasking cheat.
I call a Script, hang.php, that just hangs for 90 seconds: sleep(90)
.
You may 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
Host: ispeedlink.com
";
fwrite($stream, $http);
}
else {
echo "$id Failed
";
}
Additional scripts can be run by adding the stream: $sockets[$id++] = $stream;
Below will put anything read in to the $result[$id]
array.
Monitor the 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') . "
";
fclose($r);
unset($sockets[$id]);
}
else {
$result[$id] .= $data;
}
}
}
else {
echo 'Timeout: ' . date('h:i:s') . "
";
break;
}
}
echo system('ps auxww');
.
When I want to kill a process I use system('ps auxww')
to get the pid and kill it with system("kill $pid")
kill.php
header('Content-Type: text/plain; charset=utf-8');
//system('kill 220613');
echo system('ps auxww');