发送HTTP响应后继续执行PHP

Vic*_*let 62 php fork

如何让PHP 5.2(作为apache mod_php运行)向客户端发送完整的HTTP响应,然后继续执行操作一分钟?

长篇故事:

我有一个PHP脚本,必须执行一些长数据库请求并发送电子邮件,运行需要45到60秒.该脚本由我无法控制的应用程序调用.我需要应用程序报告从PHP脚本收到的任何错误消息(主要是无效的参数错误).

应用程序的超时延迟小于45秒(我不知道确切的值)因此将PHP脚本的每次执行都注册为错误.因此,我需要PHP尽可能快地将完整的HTTP响应发送到客户端(理想情况下,只要输入参数已经过验证),然后运行数据库和电子邮件处理.

我正在运行mod_php,所以pcntl_fork不可用.我可以通过将要处理的数据保存到数据库并运行实际过程来解决这个问题cron,但我正在寻找一个更短的解决方案.

pov*_*asp 49

我在我的"特殊脚本"工具箱中有这个片段,但它丢失了(当时云不常见),所以我正在搜索它并提出这个问题,惊讶地发现它丢失了,我搜索了更多来了回到这里发布:

<?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)

我实际上在很少的地方使用它.它完全有道理:银行链接正在返回成功支付的请求,我必须调用大量服务并在发生这种情况时处理大量数据.这有时需要10秒以上,但是banklink有固定的超时时间.因此,我承认银联并告诉他出路,并在他已经离开时做我的事情.

  • 应该有`ignore_user_abort(true);`而不是`ignore_user_abort();` (4认同)
  • 它不适用于linux上的php5和chrome浏览器,chrome在终止连接之前等待30秒 (3认同)
  • 我建议添加````session_write_close();``````flush();```如果你正在使用会话,否则你将无法使用你的网站(在同一个浏览器选项卡中)直到你的(背景)处理完成. (2认同)
  • `ignore_user_abort(); //可选`将不起作用,而无需传递函数返回当前设置的值(布尔值)。 (2认同)
  • 我在共享主机上测试了这个解决方案,等待30秒后显示"文本用户永远不会看到". (2认同)

Ale*_*sky 26

让处理初始请求的脚本在处理队列中创建一个条目,然后立即返回.然后,创建一个单独的进程(可能通过cron),定期运行队列中待处理的任何作业.

  • 这是我最初想到的解决方案。另一方面,为了解决第三方应用程序中的超时问题而设置处理队列让我感到有些不安。 (2认同)
  • 这个解决方案缺乏并行性……或者需要启动一个工作进程池来为队列提供服务。我最终发布然后断开了对 self-localhost 的 http 请求(以 SomeGuy 此处描述的方式),以利用现有 httpd 工作线程池作为后台处理器。 (2认同)

Yan*_*hon 7

你需要的是这种设置

替代文字

  • +1 哇,很棒的图表!但我认为 websocket 更好,而不是用户不断请求状态。 (2认同)

小智 7

可以对自己或任何其他脚本使用"http fork".我的意思是这样的:

// parent sript, called by user request from browser

// create socket for calling child script
$socketToChild = fsockopen("localhost", 80);

// HTTP-packet building; header first
$msgToChild = "POST /sript.php?&param=value&<more params> HTTP/1.0\n";
$msgToChild .= "Host: localhost\n";
$postData = "Any data for child as POST-query";
$msgToChild .= "Content-Length: ".strlen($postData)."\n\n";

// header done, glue with data
$msgToChild .= $postData;

// send packet no oneself www-server - new process will be created to handle our query
fwrite($socketToChild, $msgToChild);

// wait and read answer from child
$data = fread($socketToChild, $dataSize);

// close connection to child
fclose($socketToChild);
...
Run Code Online (Sandbox Code Playgroud)

现在的子脚本:

// parse HTTP-query somewhere and somehow before this point

// "disable partial output" or 
// "enable buffering" to give out all at once later
ob_start();

// "say hello" to client (parent script in this case) disconnection
// before child ends - we need not care about it
ignore_user_abort(1);

// we will work forever
set_time_limit(0);

// we need to say something to parent to stop its waiting
// it could be something useful like client ID or just "OK"
...
echo $reply;

// push buffer to parent
ob_flush();

// parent gets our answer and disconnects
// but we can work "in background" :)
...
Run Code Online (Sandbox Code Playgroud)

主要想法是:

  • 用户请求调用的父脚本;
  • parent在同一服务器(或任何其他服务器)上调用子脚本(与父级或其他服务器相同)并向其提供请求数据;
  • 父母对用户说好,结束;
  • 孩子的作品.

如果你需要与孩子互动 - 你可以使用DB作为"通信媒介":父母可以阅读孩子状态和写命令,孩子可以阅读命令和写状态.如果您需要多个子脚本 - 您应该在用户端保留子ID以区分它们,并在每次要检查相应子项的状态时将该ID发送给父项.

我在这里找到了 - http://linuxportal.ru/forums/index.php/t/22951/


Sor*_*Cat 5

如果在文件服务器上调用脚本执行就像在命令行中触发一样呢?你可以用PHP的exec来做到这一点.


Rin*_*g Ø 5

您可以使用 PHP 函数register-shutdown-function在脚本完成与浏览器的对话执行某些操作。

另请参阅ignore_user_abort - 但如果您使用 register_shutdown_function,则不需要此功能。在同一页面上,set_time_limit(0)将防止您的脚本超时。