【问题标题】:Cache problem? Server side events work in localhost, not in production enviroment缓存问题?服务器端事件在本地主机中工作,而不是在生产环境中
【发布时间】:2021-10-03 18:57:08
【问题描述】:

我想用一个简单的例子来问这个问题。 (我会在文末写下)。

我读过这个: server sent events not updating until script is finished

但我不知道如何解决。

其答案 (https://stackoverflow.com/a/37690766/8494053) 的解决方案完美运行,所以可能是我的生产服务器中的缓存问题(共享虚拟主机)。

我在最后同时收到所有的 EventStream。

我已经检查了以下所有组合:

header('Cache-Control: no-cache, no-store, must-revalidate, private, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');

但没有运气

任何人都知道如何在没有“str_pad($message, 800000)”的情况下解决这个问题?

有什么线索可以比较我的服务器的 localhost 配置和共享的 hostweb 服务器吗?

谢谢,

注意 1:两个环境中的 php 版本 8。我检查了我在共享网络服务器中使用 apached 作为开发环境和 CGI​​/FastCGI。有关系吗? 我发现了这个: Event Source -> Server returns event stream in bulk rather then returning in chunk

注意 2:两个服务器中的输出缓冲相同:output_buffering 4096

这是一个在我的主机中不起作用的简单示例:

test.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <br />
        <input type="button" onclick="startTask();"  value="Start Long Task" />
        <input type="button" onclick="stopTask();"  value="Stop Task" />
        <br />
        <br />
          
        <p>Results</p>
        <br />
        <div id="results" style="border:1px solid #000; padding:10px; width:300px; height:250px; overflow:auto; background:#eee;"></div>
        <br />
          
        <progress id='progressor' value="0" max='100' ></progress>  
        <span id="percentage" style="text-align:right; display:block; margin-top:5px;">0</span>
    </body>
</html>

<script>
var es;
  
function startTask() {
    if (!!window.EventSource) {
        es = new EventSource('long_process.php');
        //a message is received
        es.addEventListener('message', function(e) {
            var result = JSON.parse( e.data );
            addLog(result.message);  
            if(e.lastEventId == 'CLOSE') {
                addLog('Received CLOSE closing');
                es.close();
                var pBar = document.getElementById('progressor');
                pBar.value = pBar.max; //max out the progress bar
            }
            else {
                var pBar = document.getElementById('progressor');
                pBar.value = result.progress;
                var perc = document.getElementById('percentage');
                perc.innerHTML   = result.progress  + "%";
                perc.style.width = (Math.floor(pBar.clientWidth * (result.progress/100)) + 15) + 'px';
            }
        });

        es.addEventListener('error', function(e) {
          addLog('Error occurred');
          es.close();
        });        
    }
}
    
function stopTask() {
    es.close();
    addLog('Interrupted');
}

function addLog(message) {
    var r = document.getElementById('results');
    r.innerHTML += message + '<br>';
    r.scrollTop = r.scrollHeight;
}
</script>

long_process.php

<?php
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache'); 
  
function send_message($id, $message, $progress) {
    $d = array('message' => $message , 'progress' => $progress);
    echo "id: $id" . PHP_EOL;
    echo "data: " . json_encode($d) . PHP_EOL;
    echo PHP_EOL;
    //push the data out by all force possible
    ob_flush();
    flush();
}
  
//LONG RUNNING TASK
for($i = 1; $i <= 10; $i++) {
    send_message($i, 'on iteration ' . $i . ' of 10' , $i*10); 
    sleep(1);
}
send_message('CLOSE', 'Process complete', 100);
?>

关于@Tigger 答案的更新:我用过这段代码,但没有运气。我再次在脚本末尾(10 秒)连续收到所有信息,而不是每 1 秒收到一条消息。 (我还检查了“\n”和 PHP_EOL)。

function send_message($id, $message, $progress) {
    $d = array('message' => $message , 'progress' => $progress);
      
    echo "id: $id" . "\n";
    echo "data: " . json_encode($d) . "\n";
    echo "\n";
      
    //push the data out by all force possible
    while(ob_get_level() > 0) {
        ob_end_flush();
    }
    flush();
}

更新关于第二个@Tigger 答案 我用过MDN sample on GitHub,但没有运气。 XAMPP 工作,我的生产网络服务器......没有。


关于托管服务提供商的更新 由于我没有找到解决方案,我已经联系了我的共享虚拟主机,这是他们的回答:

(用谷歌翻译):

您好,在分析案例后,我们已经能够验证,在像我们这样的平台上使用 SSE,在 apache 之前使用 nginx 代理,需要在托管的 nginx 配置中进行某些自定义,这使得与共享主机服务不兼容。您需要更可定制的服务,例如 vps 或虚拟专用服务器或类似服务。问候,

由于我无法更改 nginx 配置,我的 php 文件或 javascript 中是否有任何其他配置/命令可以帮助我?

【问题讨论】:

  • 这台服务器可能只提供fastCGI,而不提供CGI
  • @Joseph 非常感谢您的回答,我如何查看您的要求?如果只是 fastCGI,我会找到解决方案吗?不是?只有当它是CGI?谢谢。

标签: javascript php server-sent-events event-stream


【解决方案1】:

经过一番折腾,我发现您的long_process.php 的以下语法在我的环境中效果最好。

我的服务器使用的是 FreeBSD,我的 PHP 脚本 (PHP 8) 也是 Unix 格式的(对于换行很重要)。如果您同时使用 Windows 和 Linux,则您的线路返回可能是问题的一部分。

我还发现ob_get_level() 帮助很大。 connection_aborted() 检查也会更快地关闭脚本。这将阻止脚本在用户离开时继续执行,将资源返回给网络服务器。

我的 JavaScript 结构也与你的有点不同,但你的问题似乎出在 PHP 方面,所以我跳过了那部分。

long_process.php

// how long between each loop (in seconds)                                                                                                                  
define('RETRY',4);

header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");

// skip the first check as the member just started
echo 'retry: '.(RETRY * 1000);
echo 'data: {"share":true,"update":false}';
echo "\n\n";

flush();
sleep(RETRY);

while(1) {
    if (... some conditional check here ...) {
        echo 'data: {"share":true,"update":true}';
    } else {
        echo 'data: {"share":true,"update":false}';
    }
    echo "\n\n";

    while(ob_get_level() > 0) {
        ob_end_flush();
    }

    flush();

    if (connection_aborted()) {
        break;
    }

    sleep(RETRY);
}

【讨论】:

  • 嗨,@Tigger 非常感谢您的回答。我的 long_process.php 只是一个例子。我试过你的伪代码,但没有运气。我也尝试过使用 PHP_EOL 或 "\n" 的两个版本。请参阅我更新的问题。
  • @JuanRangel :有一个MDN sample on GitHub。示例代码在您的两种环境中都可以工作吗?
  • 嗨@Tigger,非常感谢您的帮助,但是......没有运气。我完全使用了 github 上的代码。在我的 XAMPP 测试环境中,它可以正常工作。我可以看到它们之间随机时间的消息。在生产网络服务器中......什么都没有。即使我单击“关闭连接”也没有。 ?
【解决方案2】:

根据this answer 的类似问题,这是一个 Nginx 问题。您可以通过在响应中添加值为“no”的“X-Accel-Buffering”标头来解决此问题。有关详细信息,请参阅 Nginx 文档中的this entry

【讨论】:

  • 谢谢@TrojanName 它不起作用,可能是我的共享服务器受限。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 2020-08-16
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多