Nginx 驱动的 PHP 网站上的 SSE 似乎有一些细微差别。首先,我必须在 Nginx 配置的 Location 部分给出这个设置
fastcgi_buffering off;
有人建议我将 fastcgi_read_timeout 更改为更长的时间,但它并没有真正帮助......或者我可能没有深入了解
fastcgi_read_timeout 600s;
这两个设置都将在 Nginx 配置的 location 部分中给出。
许多人在 SSE 代码中推荐的标准无限循环往往会挂起 Nginx(或可能是 PHP7.4fpm),这很严重;因为它会关闭整个服务器。尽管人们建议在 PHP 中使用 set_time_out(0) 来更改默认超时(我相信是 30 秒),但我不太确定这是一个好策略
如果完全移除无限循环,SSE 系统似乎就像轮询一样工作:EventSource 的 Javascript 代码不断回调 SSE PHP 模块。这使得它比 Ajax 轮询更简单(因为我们不必为 Javascript 编写任何额外的代码来进行轮询),但是它仍然会继续重试,因此与 Ajax 轮询非常相似。而且每次重试都是一次完整的重新加载 PHP SSE 代码,所以它比我最终做的要慢。
这对我有用。这是一种混合解决方案,可以有一个循环,但不是无限循环。一旦该循环完成,SSE PHP 代码就会终止。这在浏览器中注册为失败(您可以在检查器控制台中看到),然后浏览器在服务器上再次调用 SSE 代码。这就像轮询,但间隔更长。
在 SSE 的一次加载和下一次重新加载之间,SSE 继续循环工作,在此期间可以将额外的数据推送到浏览器中。所以你确实有足够的速度,没有整个服务器挂起的头疼。
<?php
$success = set_time_limit( 0 );
ini_set('auto_detect_line_endings', 1);
ini_set('max_execution_time', '0');
ob_end_clean();
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
//how fast do you want the browser to reload this SSE
//after the while loop fails:
echo "retry: 200\n\n";
//If any dynamic data comes into your application
//in this 'retry' time period, and disappears,
//then SSE will NOT be able to push that data
//If it is too short, there may be insufficient
//time to finish some work within the execution
//of one loop of the SSE while loop below
$emptyCount = 0;
$execCount = 0;
$countLimit = 60; //Experiment with this, which works for you
$emptyLimit = 5;
$prev = "";
while($execCount < $countLimit){
$execCount++;
if( connection_status() != CONNECTION_NORMAL or connection_aborted() ) break;
if(file_exists($file_path)) {
//The file is to be deleted
//so that it does not return back again
//There can be better method than one suggested here
//But not getting into it, as this is only about SSE overall
$s= file_get_contents("https://.....?f=$file_path");
if($s == "")
{
$emptyCount++;
$prev = "";
}
else {
if($s != $prev){
$prev = $s;
echo $s; //This is formatted as data:...\n\n
//as needed by SSE
}
}
//If it is continually empty then break out of the loop. Why hang around?
if($emptyCount >$emptyLimit) {
$emptyCount=0;
$prev = "";
break;
}
} else $prev = "";
@ob_flush();
@flush();
sleep(1);
}