Press "Enter" to skip to content

使用fastcgi_finish_request提高页面响应速度

当PHP运行在FastCGI模式时,PHP FPM提供了一个名为fastcgi_finish_request的方法.按照文档上的说法,此方法可以提高请求的处理速度,如果有些处理可以在页面生成完后再进行,就可以使用这个方法.
听起来可能有些茫然,我们通过几个例子来说明一下:

<?php
echo '例子:';
fastcgi_finish_request(); /* 响应完成, 关闭连接 */
/* 记录日志 */
file_put_contents('log.txt', '生存还是毁灭,这是个问题.');
?>

通过浏览器访问此脚本, 结果发现并没有输出相应的字符串,但却生成了相应的文件.由此说明在调用fastcgi_finish_request后,客户端响应就已经结束,但与此同时服务端脚本却继续运行!
合理利用这个特性可以大大提升用户体验,趁热打铁再来一个例子:

<?php
echo '例子:';
file_put_contents('log.txt', date('Y-m-d H:i:s') . " 上传视频\n", FILE_APPEND);
fastcgi_finish_request();
sleep(1);
file_put_contents('log.txt', date('Y-m-d H:i:s') . " 转换格式\n", FILE_APPEND);
sleep(1);
file_put_contents('log.txt', date('Y-m-d H:i:s') . " 提取图片\n", FILE_APPEND);
?>

代码里用sleep模拟一些耗时的操作,浏览时没有被堵塞,程序却都执行了,具体看日志.
末了给您提个醒,Yahoo在Best Practices for Speeding Up Your Web Site中提到了Flush the Buffer Early,也就是利用PHP中的flush方法把内容尽快发到客户端去,它和本文介绍的fastcgi_finish_request有些许的类似.
转载附言: 我看了下这个方法, 在调用的时候, 会发送响应, 关闭连接. 但是不会结束PHP的运行. 相比调用flush, 或者我之前介绍的加速你的Echo来说, 这个方法能更加干脆一些.
另外, 从代码的可移植性讲的话, 可以在代码中附上如下代码:

if (!function_exists("fastcgi_finish_request")) {
      function fastcgi_finish_request()  {
      }
}

不会造成代码部署在非fpm环境下造成问题.

25 Comments

  1. […] 返回值: 成功时返回TRUE,失败时返回FALSE 鸟哥的例子:(https://www.laruence.com/2011/04/13/1991.html)、 <?php echo ‘你看的见我。‘; fastcgi_finish_request(); /* 响应完成, […]

  2. Duron
    Duron April 14, 2017

    max_execution_time会影响它的执行吗?

  3. bigbol
    bigbol March 25, 2015

    这个和 comet 用途有什么区别?

  4. 1031 exchange
    1031 exchange May 18, 2014

    It’s going to be ending of mine day, but before ending I am
    reading this fantastic paragraph to increase my experience.
    Look at my web-site … 1031 exchange

  5. achun
    achun December 1, 2012

    我用独立的php 文件
    简单写了个
    sleep 150
    <?php
    echo fastcgi_finish_request();
    set_time_limit(0);
    sleep(150);
    exit;
    却没有阻塞的情况,应该是唯一入口中某种情况造成的,但是是什么呢???

  6. achun
    achun December 1, 2012

    哦。刚才又测试了下 直接用 sleep 进行延时
    问题依旧,不过这次换浏览器不阻塞了
    偶尔同一个浏览器会有响应
    阻塞超时返回的是 504 Gateway Time-out

  7. achun
    achun December 1, 2012

    实际应用中发现一个问题
    fastcgi_finish_request 后
    如果脚本执行时间长,在执行结束之前,触发此事件的机器,无论切换浏览器,再也打不开执行相同php(唯一入口)的页面了,直到脚本执行完成才行,被阻塞了
    具体环境
    nginx+php 5.4
    脚本中 fastcgi_finish_request 后,是执行curl的大量循环。
    但是服务器肯定是运行好好的,如果不是访问那个唯一入口php,或者换一台机器就没有阻塞问题
    浏览器返回的错误是网管错误

  8. 雪候鸟
    雪候鸟 June 10, 2011

    @nroe 用__destruct干啥呢?

  9. nroe
    nroe June 8, 2011

    为啥不用 public function __destruct() ?

  10. htf
    htf May 9, 2011

    不错啊,很有帮助,不过Mod_php不能使用。
    fastcgi_finish_request之后的代码,虽然不占Web服务器时间,快速响应。后边的代码还是要占用fastcgi进程的时间。可以用异步事件模式。将请求插入到高速消息队列中,然后在守护进程中完成。
    http://www.swoole.com/news/55.html

    • 何忠利
      何忠利 August 22, 2018

      活捉一只大佬

  11. lotte
    lotte April 25, 2011

    很不错的学习资料,能在自己网站上尝试。或许能少走不少歪路。

  12. fy
    fy April 23, 2011

    @CFC4N 你的这个例子应该使用异步处理来完成:)
    这步应该做异步队列的插入,而不是连接smtp。。。

  13. winting
    winting April 22, 2011

    那如何知道程序执行完?

  14. wintng
    wintng April 21, 2011

    这样一下,像登录的话,可能涉及很多复杂的东西可以我们可以放在”伪后台执行(fastcgi_finish_request)”,
    但是如果数量多的话, 就会出现超时的现象, 可以在头部加入set_time_limit(0);

  15. 怪物宝
    怪物宝 April 14, 2011

    很不错啊,受教

  16. ryan
    ryan April 13, 2011

    嗯,尽量早的flush,我也觉得很好,facebook的pagelet模式我已经开始用了,感觉不错~~~

  17. CFC4N
    CFC4N April 13, 2011

    这确实是太好了,这个优点可以用于那些长时间执行的代码转为”后台执行”。类似制造了一个“任务中间层”去执行这些耗时的操作,比如用户找回密码的时候,可以直接返回找回成功,后台连接SMTP发送邮件,连接SMTP所耗时完全不需要用户等待返回,可先告诉用户已经完成,提高用户体验。

Comments are closed.