I have a php application running on IIS 7.5 , php 5.4 running as fastcgi. The application works absolutley fine with the exception that long running php scripts seem to hang; no 500 error they simply seem never complete and return the results to the browser.
I've written a simple test script below to eliminate the possibility of programming error in the main app :
<?php
/* test timeout */
/*set_time_limit(110);*/
echo "Testing time out in seconds\n";
for ($i = 0; $i < 175; $i++) {
echo $i." -- ";
if(sleep(1)!=0)
{
echo "sleep failed script terminating";
break;
}
}
?>
If I run the script beyond 175 seconds it hangs. Below that it will return the results to the browser.
Here are the time out parameters that I've set for php and fastcgi. I've also played around setting these really low in order to get various time out errors and have succeeded which brings me to the conclusion that there's another setting that I'm missing .. perhaps.
Correct me if my presumptions are wrong but would it be fair to say that if a script times out the browser will get an error where as if a script is killed off by IIS before completion then nothing is returned to the browser and you get a what looks like a hang as far as the browser is concerned?
fastcgi
activity timeout=800 Idle Timeout = 900 request Timeout 800
Php
max_execution_time=700
If a long running script doesn't communicate with the browser , after 180 seconds or so most browsers will become unresponsive to the server returning the results. The server script wasn't hanging or being terminated, it was the browsers (ie,ff and chrome) becoming unresponsive.
To check this , I ran my script and watched the status of the request.
IIS manager-> select the server-> select worker processes(central pane)->select the application pool-> select view requests (right hand pane) and watched the status and time elapsed columns. You will have to repeatedly click "show all" to see the values updating.
The state changed from ExecuterequestHandler to Sending response and then the script finished as it should but the browsers still looked like they were waiting for the server to respond.
I updated my test script from the above to this to ensure the browsers were being fed responses regularly:
<?php
#ini_set("output_buffering", "Off");
#ini_set('implicit_flush', 1);
#ini_set('zlib.output_compression', 0);
#ini_set('max_execution_time', 800);
header( 'Content-type: text/html; charset=utf-8' );
echo "Testing time out in seconds\n";
for ($i = 0; $i < 600; $i++) {
echo $i." -- ";
if(sleep(1)!=0)
{
echo "sleep failed script terminating";
break;
}
flush();
ob_flush();
}
?>
The output was't coming back to the browsers bit buy bit as it should and the problem remained.
Next step , I looked at response buffering on the server. The setting was set to a very high number and meant that flushing wouldn't work. So I set ResponseBufferLimit to 0 as per the instructions provided by #Dario in PHP flush stopped flushing in IIS7.5
This solved the problem :) If this solution has helped you please visit the above question and give Dario another +1 from me please, and maybe one to the OP for his question and script.
Thanks
Related
I'm trying to run a loop every second for 25 seconds basically.
for($i = 0; $i <= 25; $i += 1){
echo $i;
sleep(1)
}
The thing is it doesn't output until it's fully done, so after the loop continues 25 times. Is there a way to do this so it will output before each sleep? and not wait until the full loop is complete?
Thanks!
I just hashed through this same problem from a beginner perspective and came up with this bare-bones script which will do what you want.
<?PHP
ob_start();
$buffer = str_repeat(" ", 4096)."\r\n<span></span>\r\n";
for ($i=0; $i<25; $i++) {
echo $buffer.$i;
ob_flush();
flush();
sleep(1);
}
ob_end_flush();
?>
Questions that you may ask could be here (about \r\n) and here (about ob_flush()). Hope that helps you out.
What you're trying to achieve is incremental output to the browser from PHP.
Whether this is achievable can depend on your server and how you're invoking PHP.
PHP under FastCGI
You're probably a bit more likely to run into this kind of problem when PHP is running under FastCGI rather than as an Apache module, because the coupling between the server and the PHP processes is not as tightly coupled. FastCGI communication uses output buffering once the data has left the PHP processes, with the output sent to the browser only once the request is fully complete, or this buffer has filled up. On top of this, the PHP processes tend to be terminated after a certain amount of time, to avoid letting any one run for too long.
That said, a combination of ob_end_flush() (or ob_flush()) and flush() should still cause PHP to request that the downstream buffers are cleared, so this may still work. You may need to also investigate whether you need to length the time limit for PHP scripts.
PHP under mod_php
If you're using mod_php, you can write incrementally out to the browser. Use the flush() command to ensure that the PHP module will flush it instantly. If you don't have output buffering, or some Apache module such as mod_gzip, then it should go out instantly to the user's browser. What's more, you can keep your PHP script running as long as you like (with set_time_limit() in PHP), under the default configurations, though of course it will consume some memory.
You may run into trouble with some browsers which don't start rendering the page until a certain amount of a page is downloaded. Some versions of IE may wait for 1KB. I've found that Chrome can wait for more. A lot of people get around this by adding padding, such as a long comment 1 or 2 KB long at the top of the document.
Call flush will force PHP to push all of the output buffer to the client before proceeding.
for($i = 0; $i <= 25; $i += 1){
echo $i;
flush();
sleep(1);
}
EDIT:
After testing this on my lighttpd server I noticed that it buffered my outputs in blocks of 4096 characters, and I assume other browser might have similar buffering schemes. Also GZIP can prevent flush completely. Unfortunately there is no way to test that it's working due to the nature of HTTP.
Also another issue with this strategy is that it leaves that PHP proc blocked to other requests. This can cause requests to pile up.
I'm currently learning PHP so I'm doing some training, and I've come to a problem that I don't understand.
I wrote a few lines of code on Repl.it :
$msg = "Once upon a time...";
for ($i=0; $i < strlen($msg); $i++) {
echo '<span>'.$msg[$i].'</span>';
usleep(100000);
}
Here the code is running as intended (at least as I wanted it to), which is, one character is displayed after the other with a few milliseconds of delay between each. Like in RPG dialog boxes, for instance.
Now I imported my code to Visual Studio and ran it with Xampp on localhost, and everything is displayed only after the whole loop is done working.
So, in this case, with "Once upon a time...", which is 19 character long, it loads for 100.000 ms * 19, and then I have the whole message displayed at once.
Any hint for me? :)
PHP runs on the server. By default, the server waits until the page is totally generated before sending the result to the client.
You can use a combination of ob_flush() (flush the PHP output buffer) and flush() (flush the system output buffer) to achieve what you want :
$msg = "Once upon a time...";
for ($i=0; $i < strlen($msg); $i++) {
echo '<span>'.$msg[$i].'</span>';
usleep(100000);
ob_flush();
flush();
}
However, I think it would be better to be as quick as possible on the server side, and let javascript handles the fancy rendering on the client side.
I have read and deeply understood these:
http://www.php.net/manual/en/features.connection-handling.php
http://www.php.net/manual/en/function.register-shutdown-function.php
However, I have tested both PHP 5.1.6 and 5.3 and things DON'T work as described there. What I observe is:
connection_status() always return true, even after the client has closed the connection.
execution of the script keeps going on after the client has closed the connection, even though ignore_user_abort is 0
a function registered with register_shutdown_function() is not run until the script reaches ends. The script is NOT interrupted (and hence the function not called) when the client aborts the connection.
So basically PHP just doesn't detect the client's disconnection AT ALL.
Note that this is NOT as if ignore_user_abort was set to 1: if that was the case then connection_status() would return 1 even though the script would keep running and the shutdown function would not be called until the end. That is not the case.
ini_get("ignore_user_abort") returns 0, as expected.
Is this a bug in PHP, or may this be due to some Apache setting?
How do I get PHP to work as described in the abovementioned documentation?
Test script:
<?php
function myShutdown() {
error_log("myShutdown ".connection_status()." ".ini_get("ignore_user_abort"));
}
register_shutdown_function(myShutdown);
echo "Hi!";
error_log(" *** test/test *** ");
for ($i=0; $i<10; $i++) {
sleep(1);
error_log(".");
echo ".";
}
?>
Steps to reproduce:
- visit the url of the script
- abort the connection on the client before 10 seconds have elapsed (e.g. hit the stop button in the browser)
Expected/Desired behavior:
The logs should show less than 10 dots, and at the end "myShutdown 1 0" (if you watch the log in real time, the myShutDown should appear immediately when the client disconnects)
Observed/current behavior:
The logs show always exactly 10 dots, and at the end "myShutdown 0 0" (if you watch it in realtime, it goes on for 10 seconds no matter when the client disconnects).
First, I also failed to get it to work, using the basic ubuntu 12.04 LAMP installation (php5.3). But I've some information and hope that it is helpful. Any comments or edits appreciated! :)
I see two problems with your code. The first is a syntax error. You are missing the single quotes around myShutdown when calling register_shutdown_function(). Change the line to:
register_shutdown_function('myShutdown');
The second problem I see is the missing flush() call after echos. The documentation says:
PHP will not detect that the user has aborted the connection until an attempt is made to send information to the client. Simply using an echo statement does not guarantee that information is sent, see flush().
But even flush() will not help in any case. From the documentation of flush():
flush() may not be able to override the buffering scheme of your web server and it has no effect on any client-side buffering in the browser. It also doesn't affect PHP's userspace output buffering mechanism. This means you will have to call both ob_flush() and flush() to flush the ob output buffers if you are using those.
Several servers, especially on Win32, will still buffer the output from your script until it terminates before transmitting the results to the browser.
Server modules for Apache like mod_gzip may do buffering of their own that will cause flush() to not result in data being sent immediately to the client.
Even the browser may buffer its input before displaying it. Netscape, for example, buffers text until it receives an end-of-line or the beginning of a tag, and it won't render tables until the tag of the outermost table is seen.
Some versions of Microsoft Internet Explorer will only start to display the page after they have received 256 bytes of output, so you may need to send extra whitespace before flushing to get those browsers to display the page.
In the comments of the that page there is an advice to set several headers and apache configs:
apache_setenv('no-gzip', 1);
ini_set('zlib.output_compression', 0);
ini_set('implicit_flush', 1);
however, even this didn't work for me. I've investigated this using wiresharek, although the web server sends content ('Hi') after 0.0037 seconds, the web browser was buffering the page.
Good day.
For first, sorry for my bad English =)
So. I created script:
<?
sleep(10);
?>
My Apache has MPM module, I obviously didn't use sessions in this script, just.. just sleep(10).
When I open 2 tabs in my browser simultaneously, first tab loads in 10 seconds, second tab - 20 seconds.
But. When I open this script in 2 different browsers at the same time, it loads in each one after 10 seconds.
So, I started thinking, that my problem is "Connection: Keep-Alive". I changed my script:
<?
header('Connection: close');
phpinfo();
sleep(10);
?>
phpinfo() - to be sure, that headers were sent before sleep(). Buuuut... I meet the same problem. In first tab of Chrome I get headers with "Connection: close", in second tab I can't get response headers while first script is not ended. In two different browsers - everything is normal.
And now I have absolutely no ideas what I'm doing wrong. Why Chrome can't make 2 parallel queries to my site? What I should do to solve this problem?
P.S. I don't want to disable keep-alive for all my site. I don't mind, if it will speed up loading of css, images and other stuff. Even other scripts. But I want to have ability to run some scripts parallel in one browser.
P.P.S. For example: at the one page will be very long ajax query, for example - processing some big data at server-side and ajax queries with some little interval - to get status of executing first query. Obviously, that they must be parallel.
I know it's an old question but I just had the same problem and solved it with session_write_close()!
Without it PHP purposely queues scripts for same session.
Simplest possible example:
Looong Script #1:
<?php
$_SESSION['progress'] = 0;
for ($i=0; $i < 100; $i++)
{
session_start();
$_SESSION['progress']++;
session_write_close();
sleep(1);// This is slowing script purposely!
}
?>
Short script #2:
<?php
session_start();
print_r($_SESSION['progress']);
?>
Now try it, open first script that takes ages open second script in new tab and get the progress updated in a blink while first still running!! So easy right?! ;)
Same principle for ajax polling long script and second ajax call to get the progress!
I have this PHP code:
<?php
include_once("connect_to_mysql.php");
$max=300;
while($max--)
{
sleep(1);
doMyThings();
}
?>
it is supposed to repeat a mysql query 300 times with gap of 1 second between each. But the problem is after a minute or so in the browser i get this message: No Data Received. Unable to load the webpage because the server sent no data.
The problem is the following: Your code will at least (without considering the amount of time needed by doMyThings()) last 300 seconds. Most PHP environments set the default script running time to about 60 secs, the script stops and nothing is printed out.
Next thing is (if script execution time is set high enough to allow long running scripts), the script has to run until its finished (that is, ~300 secs) and after that, data is written onto the output stream. Until there, you won't see any output.
To circumvent those two problems, see this code:
<?php
// If allowed, unlimited script execution time
set_time_limit(0);
// End output buffering
ob_end_flush();
include_once("connect_to_mysql.php");
$max=300;
// End output buffering IE and Safari Workaround
// They will only display the webpage if it's completely loaded or
// at least 5000 bytes have been "printed".
for($i=0;$i<5000;$i++)
{
echo ' ';
}
while($max > 0)
{
sleep(1);
doMyThings();
$max--;
// Manual output buffering
ob_flush();
flush();
}
?>
Maybe this post is also of interest to you: Outputting exec() ping result progressively
The browser will not wait a whole 5 minutes for you to complete your queries.
You need to find a different solution. Consider executing the PHP script in CLI.
It seems that you have a timeout executing 300 times doMyThings();
You can try with set_time_limit(0);
Set the number of seconds a script is allowed to run. If this is reached, the script returns a fatal error. The default limit is 30 seconds or, if it exists, the max_execution_time value defined in the php.ini.
When you execute long time php code on server side, you need change max_execution_time directive in php.ini. But browser will not wait how long as you want so you need use async technology like AJAX