发送http响应后继续处理php

use*_*214 91 php http response

我的脚本由服务器调用.从服务器我会收到ID_OF_MESSAGETEXT_OF_MESSAGE.

在我的脚本中,我将处理传入的文本并使用params生成响应:ANSWER_TO_IDRESPONSE_MESSAGE.

问题是我正在向incomming发送响应"ID_OF_MESSAGE",但是在收到http响应200之后,向我发送消息的服务器将把他的消息设置为发送给我(这意味着我可以发送给他的响应).

解决方案之一是将消息保存到数据库并制作一些将每分钟运行的cron,但我需要立即生成响应消息.

是否有一些解决方案如何发送到服务器http响应200而不是继续执行PHP脚本?

非常感谢

vca*_*lli 179

是.你可以这样做:

ignore_user_abort(true);
set_time_limit(0);

ob_start();
// do initial processing here
echo $response; // send the response
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();

// now the request is sent to the browser, but the script is still running
// so, you can continue...
Run Code Online (Sandbox Code Playgroud)

  • 提示:我开始使用PHP-FPM,所以我不得不在最后添加`fastcgi_finish_request()` (13认同)
  • 请注意,如果将内容编码标头设置为除"none"以外的任何内容,则可能会使此示例无用,因为它仍然会让用户等待完整的执行时间(直到超时?).因此,要确保它在生产环境中本地工作,请将'content-encoding'标头设置为'none':`header("Content-Encoding:none")` (7认同)
  • 是否可以通过保持活动连接来实现? (3认同)
  • 优秀!!这是对这个问题唯一有效的回答!10便士+ (3认同)
  • 是否有理由在ob_end_flush之后使用ob_flush?我理解最后需要刷新功能,但我不确定为什么在调用ob_end_flush时需要ob_flush. (3认同)
  • 真棒的答案!我唯一改变的是`set_time_limit(0);`.您可能希望它运行的时间超过默认的30秒,但如果它进入无限循环,则无限期可能会导致问题!我在`php.ini`文件中设置了更长的值. (2认同)
  • RE:保持连接:您不一定需要关闭连接,但接下来会发生同一连接上请求的*next*资产将被迫等待.所以你可以快速提供HTML,但是你的一个JS或CSS文件可能会加载缓慢,因为连接必须完成从PHP获得响应才能获得下一个资产.因此,关闭连接是一个好主意,因此浏览器不必等待它被释放. (2认同)
  • "ob_end_flush()函数; 使用ob_flush();" 产生一个警告,没有什么可以冲洗.关于'ob_flush();'的PHP文档 states:_这个函数不像ob_end_flush()那样破坏输出缓冲区._ (2认同)
  • 在“ ob_end_flush();”之后,不再需要“ ob_flush();”。实际上,这将导致错误。仅仅用ob_end_flush();就足够了。 (2认同)
  • 注意:如果您的代码中有任何其他的 ob_start() 调用没有被 ob_end_flush() 终止,这将不起作用。为了保证所有缓冲区都被刷新和结束,你可以这样做:`while (ob_get_level() > 0) { ob_end_flush(); }`。 (2认同)
  • 只有当“$response”的大小足够大时,这才开始对我起作用,因此我将“echo $response;”更改为“echo str_pad($response, 4096);”以强制使用最小大小。(背景:我需要在 3 秒内向 Web 服务返回 HTTP 200,以确认已收到 POST 请求。从技术上讲,响应不必包含任何内容。) (2认同)
  • @marcvangend:感谢您提供有关“str_pad()”的提示!我添加了对 `ini_get("output_buffering")` 的调用来获取缓冲区大小,以防它未设置为默认的 4096。 (2认同)

Kos*_*tos 39

我在这里看到很多回复建议使用,ignore_user_abort(true);但这段代码不是必需的.所有这一切都是为了确保您的脚本在用户中止(通过关闭浏览器或按下escape以停止请求)发送响应之前继续执行.但这不是你要问的.您要求在发送响应后继续执行.您所需要的只是以下内容:

    // Buffer all upcoming output...
    ob_start();

    // Send your response.
    echo "Here be response";

    // Get the size of the output.
    $size = ob_get_length();

    // Disable compression (in case content length is compressed).
    header("Content-Encoding: none");

    // Set the content length of the response.
    header("Content-Length: {$size}");

    // Close the connection.
    header("Connection: close");

    // Flush all output.
    ob_end_flush();
    ob_flush();
    flush();

    // Close current session (if it exists).
    if(session_id()) session_write_close();

    // Start your background work here.
    ...
Run Code Online (Sandbox Code Playgroud)

如果您担心您的后台工作将花费比PHP的默认脚本执行时间限制更长的时间,那么请坚持 set_time_limit(0);到顶部.

  • 尝试了很多不同的组合,这是有效的!谢谢Kosta Kontos !!! (3认同)
  • 在 apache 2、php 7.0.32 和 ubuntu 16.04 上完美运行!谢谢! (2认同)

Dar*_*ron 25

如果您正在使用FastCGI处理或PHP-FPM,您可以:

session_write_close(); //close the session
fastcgi_finish_request(); //this returns 200 to the user, and processing continues

// do desired processing ...
$expensiveCalulation = 1+1;
error_log($expensiveCalculation);
Run Code Online (Sandbox Code Playgroud)

资料来源:http://fi2.php.net/manual/en/function.fastcgi-finish-request.php

  • 数据总和昂贵计算寿:o印象深刻,这么贵! (5认同)
  • 谢谢你,花了几个小时后,这在 nginx 中对我有用 (2认同)

Ehs*_*san 17

我在这个问题上花了几个小时,我已经使用了这个适用于Apache和Nginx的函数:

/**
 * respondOK.
 */
protected function respondOK()
{
    // check if fastcgi_finish_request is callable
    if (is_callable('fastcgi_finish_request')) {
        /*
         * This works in Nginx but the next approach not
         */
        session_write_close();
        fastcgi_finish_request();

        return;
    }

    ignore_user_abort(true);

    ob_start();
    $serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING);
    header($serverProtocole.' 200 OK');
    header('Content-Encoding: none');
    header('Content-Length: '.ob_get_length());
    header('Connection: close');

    ob_end_flush();
    ob_flush();
    flush();
}
Run Code Online (Sandbox Code Playgroud)

您可以在长时间处理之前调用此函数.

  • 这真是可爱!在尝试了上面的所有其他内容后,这是唯一适用于nginx的东西. (2认同)

Jus*_*tin 6

稍微修改了@vcampitelli 的答案。不要认为您需要close标题。我在 Chrome 中看到重复的关闭标头。

<?php

ignore_user_abort(true);

ob_start();
echo '{}';
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted");
header("Status: 202 Accepted");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();

sleep(10);
Run Code Online (Sandbox Code Playgroud)

  • 我在原来的答案中提到过这一点,但我也在这里说一下。您不一定需要关闭连接,但随后会发生的情况是同一连接上请求的下一个资产将被迫等待。因此,您可以快速交付 HTML,但随后您的 JS 或 CSS 文件之一可能会加载缓慢,因为连接必须先完成从 PHP 获取响应,然后才能获取下一个资源。因此,出于这个原因,关闭连接是一个好主意,这样浏览器就不必等待它被释放。 (6认同)

Mat*_*man 6

我在 2012 年 4 月向 Rasmus Lerdorf 提出了这个问题,并引用了以下文章:

我建议开发一个新的 PHP 内置函数来通知平台不会生成进一步的输出(在标准输出上?)(这样的函数可能会负责关闭连接)。拉斯穆斯·勒多夫回应:

参见吉尔曼。您确实不希望前端 Web 服务器进行这样的后端处理。

我可以理解他的观点,并支持他对某些应用程序/加载场景的看法!然而,在其他一些场景下,vcampitelli 等人的解决方案是不错的。


Fil*_*Fil 5

我无法安装 pthread,之前的解决方案都不适合我。我发现只有以下解决方案有效(参考: https: //stackoverflow.com/a/14469376/1315873):

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush();            // Unless both are called !
session_write_close(); // Added a line suggested in the comment
// Do processing here 
sleep(30);
echo('Text user will never see');
?>
Run Code Online (Sandbox Code Playgroud)

  • 来自关于 `ob_start` 指令的 php 文档:“_当输出缓冲处于活动状态时,脚本不会发送任何输出(标头除外)_”,因此它不会改变任何内容......我会将其放在 `ob_start()` 之前只是为了不要错误地认为标头也被缓冲,正如定位似乎暗示的那样。但如果你愿意的话,即使在`ob_start()`之后它仍然可以工作。 (2认同)