关闭连接后继续处理

时间:2011-01-26 15:54:41

标签: php

PHP中是否有办法关闭连接(基本上是告诉浏览器而不是没有更多的数据)但是继续处理。我想到的具体情况是我想要提供缓存数据,然后如果缓存已过期,我仍然会提供缓存数据以获得快速响应,关闭连接,但继续处理以重新生成并缓存新数据。基本上唯一的目的是使网站看起来更具响应性,因为当用户等待内容重新生成时不会偶尔出现延迟。

更新:

PLuS对我所寻找的答案最接近。为了澄清一些人,我正在寻找能够实现以下步骤的东西:

  1. 用户请求页面
  2. 连接将打开服务器
  3. PHP检查缓存是否已过期,如果仍然是新鲜的,则提供缓存和关闭连接(在此处结束)。如果已过期,请继续执行4。
  4. 投放过期缓存
  5. 关闭连接,以便浏览器知道它不等待更多数据。
  6. PHP重新生成新数据并缓存它。
  7. PHP关闭。
  8. 更新:

    这很重要,它必须是纯粹的PHP解决方案。安装其他软件不是一种选择。

8 个答案:

答案 0 :(得分:24)

如果在fastcgi下运行,你可以使用非常好的:

fastcgi_finish_request();

http://php.net/manual/en/function.fastcgi-finish-request.php

有更详细的信息in a duplicate answer

答案 1 :(得分:14)

我终于找到了解决方案(感谢Google,我不得不继续尝试不同的搜索字词组合)。感谢来自this page的arr1的评论(它大约是页面下方的三分之二)。

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true);
ob_start();
echo 'Text the user will see';
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // All output buffers must be flushed here
flush();        // Force output to client
// Do processing here 
sleep(30);
echo('Text user will never see');

我还没有真正对此进行测试,但简而言之,你发送了两个标题:一个告诉浏览器确切的数据,然后告诉浏览器关闭连接(只有在收到连接后才会这样做)预期的内容量)。我还没有测试过这个。

答案 2 :(得分:7)

您可以通过将时间限制设置为无限制并忽略连接

来实现
<?php
ignore_user_abort(true);
set_time_limit(0);

另见:http://www.php.net/manual/en/features.connection-handling.php

答案 3 :(得分:2)

PHP没有这种持久性(默认情况下)。我能想到的唯一方法就是运行cron jobs来预先填充缓存。

答案 4 :(得分:2)

据我所知,除非你正在运行FastCGI,否则你不能放弃连接并继续执行(除非你得到了Endophage的工作答案,我失败了)。所以你可以:

  1. 使用cron或类似的东西来安排此类任务
  2. 使用子流程完成工作
  3. 但它变得更糟。即使您使用proc_open()生成子进程,PHP也会在关闭连接之前等待它完成,即使在调用exit()die()some_undefined_function_causing_fatal_error()之后也是如此。我找到的唯一解决方法是生成一个子进程,它自己生成一个子进程,如下所示:

    function doInBackground ($_variables, $_code)
    {
        proc_open (
            'php -r ' .     
                escapeshellarg ("if (pcntl_fork() === 0) { extract (unserialize (\$argv [1])); $_code }") .
                ' ' . escapeshellarg (serialize ($_variables)),
            array(), $pipes 
        );
    }
    
    $message = 'Hello world!';
    $filename = tempnam (sys_get_temp_dir(), 'php_test_workaround');
    $delay = 10;
    
    doInBackground (compact ('message', 'filename', 'delay'), <<< 'THE_NOWDOC_STRING'
        // Your actual code goes here:
        sleep ($delay);
        file_put_contents ($filename, $message);
    THE_NOWDOC_STRING
    );
    

答案 5 :(得分:1)

可以从PHP-CLI编译和运行程序(不在共享主机&gt; VPS上)

缓存

对于缓存,我不会这样做。我会使用redis作为我的LRU cache。它将very fast(benchmarks),尤其是当您使用client library written in C进行编译时。

离线处理

安装beanstalkd消息队列时,您也可以执行延迟放置。但我会使用redis brpop / rpush来完成另一个消息队列部分,因为redis会更快,特别是如果你使用PHP客户端库(在C用户空间中)。

无法从PHP-CLI编译或运行程序(在共享主机上)

参数或者set_time_limit

大多数时候这个set_time_limit不可用(因为安全模式或max_execution_time指令)至少在共享主机上设置0。也是共享主机真正提供商不喜欢的用户长时间保持PHP进程。大多数情况下,默认限制设置为30。

的Cron

使用cron使用Cache_lite将数据写入光盘。一些stackoverflow主题已经解释了这个:

也相当容易,但仍然是hacky。我认为你必须升级(&gt; VPS)就必须进行这样的黑客攻击。

异步请求

作为最后的手段,您可以使用Cache_lite执行asynchronous request缓存数据。 请注意共享主机不喜欢你支持很多长时间运行的PHP进程。我只使用一个后台进程,当它到达max-execution-time指令时调用另一个进程。我会注意到脚本启动时和几次缓存调用之间的时间我会测量花费的时间以及它何时接近我将执行另一个异步请求的时间。我会使用locking来确保只有一个进程正在运行。这样我就不会惹恼提供者而且可以做到。另一方面,我不认为我会写任何这个,因为如果你问我,它会有点hacky。当我达到那个规模时,我会升级到VPS。

答案 6 :(得分:0)

如果您这样做是为了缓存内容,您可能需要考虑使用现有的缓存解决方案,例如memcached

答案 7 :(得分:-3)

没有。就Web服务器而言,来自浏览器的请求由PHP引擎处理,就是这样。请求持续时间与PHP一样长。

你可能会fork()