Forum: NGINX FastCGI PHP - unable to prematurely close connection to browser

Posted by cactus (Guest)
on 2010-02-06 11:01
(Received via mailing list)
Hi all!

I am optimizing a few of the PHP scripts by:
- doing all that generates output to browser
- closing connection to browser
- doing some more processing

The processing bit cannot be avoided, but the speed of execution (from 
visitors' point of view) is awesome this way. The problem is that we 
have tried migrating the script to NginX + FastCGI (it works on Apache + 
mod_php) but it doesn't close the connection anymore, it keeps it open 
until the end... Which makes scripts slow again.

I am using Content-Length to allow server to figure out that all content 
was already generated. This is the test case:

<?
  header("Connection: close"); // not sure we need this one
  header("Content-Encoding: none");
  ignore_user_abort(true);
  ob_start();

  echo ('Lets output something.');

  // output Content-Length and flush buffers:
  $size = ob_get_length();
  header("Content-Length: $size");
  ob_end_flush();
  flush();

  // ****************
  // do some heavy processing here:
  sleep(5);

  function postproc() {
    flush();
    sleep(5);
  }
  register_shutdown_function('postproc');
?>


The browser shows this page immediately in Apache+mod_php but it waits 
for 10 seconds in NginX + FastCGI.

I am a bit stuck here and would appreciate some help... Is this a 
problem with FastCGI? Is there another way to do it?

Thanks!

Posted at Nginx Forum: 
http://forum.nginx.org/read.php?2,51310,51310#msg-51310
Posted by Hoang Hoang (vnjug)
on 2010-02-06 16:25
Hi,

I just tested your script and found that I needed to wait 10s to see the 
response. I am using Windows XP, Apache 2.1.11 worker MPM, PHP 5.3.1

It did not work asynchronously as you expected. Am I missing something 
here?

Regards

cactus wrote:
> Hi all!
> 
> I am optimizing a few of the PHP scripts by:
> - doing all that generates output to browser
> - closing connection to browser
> - doing some more processing
> 
> The processing bit cannot be avoided, but the speed of execution (from 
> visitors' point of view) is awesome this way. The problem is that we 
> have tried migrating the script to NginX + FastCGI (it works on Apache + 
> mod_php) but it doesn't close the connection anymore, it keeps it open 
> until the end... Which makes scripts slow again.
> 
> I am using Content-Length to allow server to figure out that all content 
> was already generated. This is the test case:
> 
> <?
>   header("Connection: close"); // not sure we need this one
>   header("Content-Encoding: none");
>   ignore_user_abort(true);
>   ob_start();
> 
>   echo ('Lets output something.');
> 
>   // output Content-Length and flush buffers:
>   $size = ob_get_length();
>   header("Content-Length: $size");
>   ob_end_flush();
>   flush();
> 
>   // ****************
>   // do some heavy processing here:
>   sleep(5);
> 
>   function postproc() {
>     flush();
>     sleep(5);
>   }
>   register_shutdown_function('postproc');
> ?>
> 
> 
> The browser shows this page immediately in Apache+mod_php but it waits 
> for 10 seconds in NginX + FastCGI.
> 
> I am a bit stuck here and would appreciate some help... Is this a 
> problem with FastCGI? Is there another way to do it?
> 
> Thanks!
> 
> Posted at Nginx Forum: 
> http://forum.nginx.org/read.php?2,51310,51310#msg-51310
Posted by cactus (Guest)
on 2010-02-07 15:51
(Received via mailing list)
First of all - thank you for your answer, I really appreciate it.

This is interesting - I have re-checked and it takes me 32ms to get the 
result on my development machine:
Server  Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny4 with Suhosin-Patch
X-Powered-By  PHP/5.2.6-1+lenny4
(PHP is installed as mod_php)

Still, it is very important for me to be able to postpone processing. If 
I can't do it on NginX I will probably have to find another server that 
can do this (overall load on the server is less important than quick 
response times in this case). I'm really hoping it's just a config 
thing... :)

>From what I understand:
- PHP offers no way to prematurely close the connection except by 
setting and reaching Content-Length
- it is the web server (NginX / Apache) that can (should?) close the 
connection in this case

I can't make PHP close the conenction, so my only hope is NginX. Can it 
be told to do so? Am I missing something?

Thanks again!



Hoang Hoang Wrote:
-------------------------------------------------------
> 
> > The processing bit cannot be avoided, but the
> figure out that all content 
> > 
> >   function postproc() {
> > 
> 51310
> 
> -- 
> Posted via http://www.ruby-forum.com/.
> 
> _______________________________________________
> nginx mailing list
> nginx@nginx.org
> http://nginx.org/mailman/listinfo/nginx

Posted at Nginx Forum: 
http://forum.nginx.org/read.php?2,51310,51672#msg-51672
Posted by Rob Schultz (schultz)
on 2010-02-07 18:05
(Received via mailing list)
Hi,
  Not really to change you method on doing things. but you still will 
encounter slow response times for users eventually since that PHP 
process is *still* processing things after that user is done and won't 
be ready for the next user until it is done processing that script. So 
theoretical if you had 5 max PHP processes to handle client requests and 
say each PHP script take 500ms to complete but only 100ms is until your 
Connection: close.  If 6 users hit your server at once. That user would 
have to wait the full 500ms until a free PHP process is available to 
process his request and then take the additional 100ms for it to see 
content. So now his experience for the page to load up is it takes 
almost 600ms.

Something i would suggest is using a messaging queue and a "few PHP 
process's that are just designed to process the queue" this allows you 
to separate your user processing and your offline processing. Which will 
also allow you to offload your work to another server if it starts 
growing and such. here is website with some links to some message 
queues. 
http://blog.fedecarg.com/2008/11/03/getting-started-with-message-queues/

v/r,
Rob
Posted by cactus (Guest)
on 2010-02-07 19:31
(Received via mailing list)
Rob, thank you for the idea! We have used it before on other problems, 
though I had no idea there are libraries made to address this problem (I 
used my own solution). Thanks for the link, the projects there look very 
promising.

However, the way I see it, this approach is only useful if you want to 
distribute the load across other machines or across time (so that major 
work is done when the load is lower). In our case the application runs 
on several machines already and we can add more if necessary. Also, the 
work MUST be done at about the time the request was issued, so it cannot 
wait 10 minutes when the load might be lower. I think in that case the 
only solution is to compute when we get the requests, with the possible 
optimization of returning the output before all work is done. Or am I 
mistaken?

When the requests start getting slow (because they are waiting for other 
requests to finish) we can just add more machines (or enlarge max. 
number of  PHP processes limit first ;) and we're done.

So, still searching for the culprit that doesn't close the connection to 
browser when Content-Length is reached... :) Any ideas?

Or is this a FastCGI issue? Is there any other way to use PHP with 
NginX?

Thanks again!

Posted at Nginx Forum: 
http://forum.nginx.org/read.php?2,51310,51782#msg-51782
Posted by Rob Schultz (schultz)
on 2010-02-07 19:47
(Received via mailing list)
> So, still searching for the culprit that doesn't close the connection to browser when Content-Length is reached... :) Any ideas? 
> 
> Or is this a FastCGI issue? Is there any other way to use PHP with NginX?

I believe this is a FastCGI issue. And nginx buffer's the FastCGI 
response until FastCGI request is completed. The Header("Connection: 
close"); is only used to prevent using Keep-alive connection. It tells 
the browser to terminate the connection and not keep them open, thus 
having no effect on when the data gets to the client.

A suggestion that might work as your needing is stripping your apache 
down to only using mod_php and anything else you might want. and 
basically use it as a PHP process manager.  Then you can just proxy your 
requests to apache for the php requests and turn proxy_buffering off 
http://wiki.nginx.org/NginxHttpProxyModule#proxy_buffering which then 
*should* produce the results you require. This allow you to use nginx 
for your static resources while just utilizing apache as just a PHP 
manager.

v/r,
Rob
Posted by cactus (Guest)
on 2010-02-07 20:48
(Received via mailing list)
I was hoping for a different answer - I guess I'll just have to make a 
few of configurations and test what works best.

Thank you for helping out! :)

Posted at Nginx Forum: 
http://forum.nginx.org/read.php?2,51310,51813#msg-51813
Posted by Tobia Conforto (Guest)
on 2010-02-08 10:25
(Received via mailing list)
cactus wrote:
> is this a FastCGI issue?

You could try asking on a PHP mailing list, or looking through the PHP 
source code, to see if there is a function call that will close the 
FastCGI connection, while keeping the process open.

Tobia
Posted by Reinis Rozitis (Guest)
on 2010-02-08 12:35
(Received via mailing list)
>>From what I understand:
> - PHP offers no way to prematurely close the connection except by setting and reaching Content-Length

While I havent it used myself so not sure if it works 100% as expected 
there is such feature if you use php with the FPM
patchset/manager - fastcgi_finish_request()

http://php-fpm.org/wiki/Features#fastcgi_finish_request.28.29


rr
Posted by cactus (Guest)
on 2010-02-14 15:29
(Received via mailing list)
Reinis Rozitis Wrote:
-------------------------------------------------------
> http://php-fpm.org/wiki/Features#fastcgi_finish_re
> quest.28.29

Thanks for the info, this is good news! :)
I hope it gets in production soon - will try it out anyway, just out of 
curiosity, I just need to find some extra time... :)

Thanks to all, enjoy!

Posted at Nginx Forum: 
http://forum.nginx.org/read.php?2,51310,54461#msg-54461
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.