当PHP使用Apache作为模块时,只要PHP生成它就输出内容很好,因为您可以在PHP中禁用output_buffering并使用flush()或implicit_flush(1)。这是我以前使用的,它工作正常。
我已经遇到问题,因为我已经切换到PHP-FPM,其中我无法让Apache(2.4)输出PHP的内容直到整个脚本完成。我仍然关闭output_buffering和刷新,但这还不够。 Apache没有使用mod_gzip(无论如何都会影响PHP模块。)
Nginx有一个禁用proxy_buffering的选项,通过阅读其他人的评论修复了这个问题,但是我找不到任何在Apache中这样做的方法。
以下是如何在Apache中调用PHP:
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php-fpm/php-fpm.sock|fcgi://localhost/"
</FilesMatch>
<Proxy fcgi://localhost/ enablereuse=on retry=0 timeout=7200 max=500 flushpackets=on>
</Proxy>
Apache文档提到了似乎需要的flushpackets(上面使用过的),但是接下来它还说它现在只适用于AJS,而不是所有代理内容所以它不会做任何事情在这种情况下。
回应足够的空格来填充缓冲区可能会有效,但这是一个混乱的解决方法,远非理想。
简而言之:有没有人知道让Apache发送PHP内容的正确方法,而不是等到脚本完成之后?
答案 0 :(得分:3)
重新发布我刚刚在这里发布的一个非常相似的问题的答案:How to disable buffering with apache2 and mod_proxy_fcgi?
一些注意事项,因为我在过去几个小时里试图找到这个问题的答案:
mod_proxy
/ mod_proxy_fcgi
时,完全禁用输出缓冲是不可能的,但是,您仍然可以在块中传输响应。mod_fastcgi
或mod_fcgi
模块禁用输出缓冲,但这些mod并不像Apache 2.4那样受欢迎/广泛使用。mod_deflate
并且未为虚拟主机/目录/等设置SetEnv no-gzip 1
。那个流数据,然后gzip将不允许缓冲区刷新,直到请求完成。我正在测试一些东西,看看如何最好地使用Drupal 8的新BigPipe功能来向客户端发送流请求,我在this GitHub issue中发布了更多注释。
答案 1 :(得分:2)
在我的环境(Apache 2.4,php-fpm)中,它在关闭压缩并将输出填充到output_buffering
时有效,请参见脚本:
header('Content-Encoding: none;');
$padSize = ini_get('output_buffering');
for($i=0;$i<10;$i++) {
echo str_pad("$i<br>", $padSize);
flush();
sleep(1);
}
答案 2 :(得分:0)
我通过重写Proxy
部分(基于this answer)成功禁用了输出缓冲:
<FilesMatch \.php$>
SetHandler "proxy:unix:/run/php-fpm/php-fpm.sock|fcgi://localhost"
</FilesMatch>
<Proxy fcgi://localhost>
ProxySet enablereuse=on flushpackets=on
</Proxy>
答案 3 :(得分:0)
https://www.php.net/manual/en/function.fastcgi-finish-request.php是拯救我理智的原因。我尝试了各种技巧和技术来使Apache和php-fpm(7.4)在浏览器中显示长时间运行的进程的进度,包括Server-Sent Events,将进度写入文本文件并使用xhr对其进行轮询, flush()
像疯了似的,等等。直到我做了这样的事情(在我的MVC动作控制器中),一切都没有起作用
public function longRunningProcessAction()
{
$path = \realpath('./data/progress.sqlite');
$db = new \PDO("sqlite:$path");
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
$stmt = $db->prepare("UPDATE progress SET status = :status");
$stmt->execute([':status' => "starting"]);
header("content-type: application/json");
echo json_encode(['status'=>'started']);
// this here is critical ...
session_write_close();
fastcgi_finish_request();
// otherwise it will NOT work
for ($i = 0; $i <= 150; $i++) {
usleep(250*1000);
$stmt->execute([':status' => "$i of 150"]);
// this also works
file_put_contents('./data/progress.txt',"$i of 150");
}
$stmt->execute([':status' => "done"]);
}
// and...
public function progressAction()
{
$path = \realpath('./data/progress.sqlite');
$db = new \PDO("sqlite:$path");
$query = 'SELECT status FROM progress';
$stmt = $db->query($query);
// and this is working as well..
$text = file_get_contents('./data/progress.txt');
return new JsonModel(['status'=>$stmt->fetchColumn(),'text'=>$text]);
}
然后是一些Javascript(jQuery)
var check_progress = function() {
$.get("/my/job/progress").then(r=>{
$("#progress").text(r.status);
if (r.status === "done") { return; }
window.setTimeout(check_progress,300);
});
$.post("/long/running/process",data).then(check_progress);
Voilà!
答案 4 :(得分:-2)
使用Apache 2.4 mod_proxy进行PHP FPM的工作:
使用没有ob_start()的ob_end_clean()对我没有意义,但它似乎有所帮助 - 它返回true(=成功!)