[英]SSE incredibly slow
我目前正在編寫一個網頁游戲的通信框架,通信圖如下: 代碼如下:
測試.php:
<!DOCTYPE html>
<html>
<head>
<title> Test </title>
<script>
function init()
{
var source = new EventSource("massrelay.php");
source.onmessage = function(event)
{
console.log("massrelay sent: " + event.data);
var p = document.createElement("p");
var t = document.createTextNode(event.data);
p.appendChild(t);
document.getElementById("rec").appendChild(p);
};
}
function test()
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function ()
{
if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200)
{
console.log("reciver responded: " + xhr.responseText);
}
}
xhr.open("GET", "reciver.php?d=" + document.getElementById("inp").value , true);
xhr.send();
console.log("you sent: " + document.getElementById("inp").value);
}
</script>
</head>
<body>
<button onclick="init()">Start Test</button>
<textarea id="inp"></textarea>
<button onclick="test()">click me</button>
<div id="rec"></div>
</body>
</html>
這需要用戶輸入(當前是一個用於測試的文本框)並將其發送到接收器,並將接收器的響應寫回控制台,我從未收到過來自接收器的錯誤。 它還為發送的 SSE 添加了一個事件偵聽器。
接收者.php:
<?php
$data = $_REQUEST["d"];
(file_put_contents("data.txt", $data)) ? echo $data : echo "error writing";
?>
如您所見,這非常簡單,僅用於在發回寫入成功之前將數據寫入 data.txt 的功能。 data.txt 只是傳遞給 massrelay.php 的“管”數據。
massrelay.php:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
while(1)
{
$data = file_get_contents("data.txt");
if ($data != "NULL")
{
echo "data: " . $data . "\n\n";
flush();
file_put_contents("data.txt", "NULL");
}
}
?>
massrelay.php 檢查 data.txt 中是否有任何數據,如果有,將使用 SSE 將其傳遞給具有事件偵聽器的任何人,一旦讀取數據,它將清除數據文件。
整個事情實際上工作得很好,除了輕微的 ishue,massrelay.php 從數據文件發送數據可能需要 30 秒到 10 分鍾。 對於網頁游戲來說,這是完全不可接受的,因為您需要實時動作。 我想知道是否由於我的代碼中的缺陷而花費了這么長時間,或者我不是在考慮硬件(我自己在 2006 年戴爾上用 sempron 托管它)。 如果有人看到它有任何問題,請告訴我,謝謝。
我看到您的代碼存在三個問題:
您的 while() 循環不斷讀取文件系統。 你需要放慢速度。 我在下面睡了半秒; 用可接受的延遲的最大值進行試驗。
PHP 有自己的輸出緩沖區。 您使用@ob_flush()
來刷新它們( @
抑制錯誤)和flush()
來刷新Apache 緩沖區。 兩者都需要,順序也很重要。
最后,PHP 會話會鎖定,因此如果您的客戶端可能正在發送會話 cookie,即使您的 SSE 腳本不使用會話數據,您也必須在進入無限循環之前關閉會話。
我已將所有三個更改添加到您的代碼中,如下所示。
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
session_write_close();
while(1)
{
$data = file_get_contents("data.txt");
if ($data != "NULL")
{
echo "data: " . $data . "\n\n";
@ob_flush();flush();
file_put_contents("data.txt", "NULL");
}
usleep(500000);
}
順便說一句,關於使用內存數據庫的另一個答案中的建議很好,但文件系統開銷以毫秒為單位,因此它不會解釋“30 秒到 10 分鍾”的延遲。
編輯:我刪除了這個答案,因為 OP 說我建議的測試 1.(見下文)工作正常,所以我關於輸出緩沖的理論是錯誤的。 但另一方面,他說與本機函數相同的代碼fread
fwrite
fclose
flock
不起作用,所以如果緩沖和文件 I/O 不是解決方案,我不知道它是什么。 我刪除了我的帖子,因為我認為這不是一個有效的答案。 讓我總結一下:
flush
工作正常fopen
fread
fwrite
flock
並且它沒有幫助。 如果flush
正常工作,文件系統正常工作,我只能相信OP他是對的並放棄。
所以知道我在這里的工作已經完成了,如果我不能在 OP 的系統、配置和代碼上自己嘗試,我無能為力。
我取消了我的答案,所以 OP 可以鏈接到文檔,其他人可以看到我嘗試提出解決方案。
1. 測試massrelay.php
while(true) {
echo "test!";
sleep(1);
}
這樣您就可以確定該問題與文件無關。
2. 確保您啟用了error_reporting
和display_errors
。
我猜你會在 30 秒后得到響應,因為 PHP 腳本在時間限制后被終止。 如果您啟用了錯誤,您會看到錯誤消息通知您。
3. 確保你真的刷新了你的輸出並且它沒有被緩沖。
可能需要 30 秒到 10 分鍾不等
您能夠在 30 秒后查看數據是有意義的,因為 30 秒是 PHP 中最大執行時間的默認值。
看起來flush()
在您的場景中不起作用,您應該檢查php.ini 文件中的output_buffering
設置
請看這個: php flush not working
文檔:
幾年前,我嘗試使用平面文件並將數據存儲在數據庫中,以便在多個並發用戶與服務器之間進行通信(這是針對 Flash 游戲,但適用相同的原則)。
平面文件的性能最差,因為您最終會遇到讀/寫訪問問題。
對於 DB,它最終也會因請求過多而崩潰,尤其是當您每秒訪問 DB 數千次並且沒有適當的負載平衡時。
我的答案不是解決您當前的問題,而是將您引向不同的方向。 您真的必須考慮使用套接字服務器。 也許看看類似的東西: https : //github.com/reactphp/socket
您在使用套接字服務器時可能會遇到的一些問題是共享主機不允許您運行 shell 腳本。 我的解決方案是使用我的家用 PC 進行套接字通信,並將我的域用作托管游戲的公共入口點。 顯然,我們並不是都有靜態 IP 來指向我們的游戲,所以我不得不使用 dyndns,當時它是免費的: http ://dyn.com(現在可能還有一些其他新服務是免費的)。 使用家庭服務器時,您還需要設置路由器進行端口轉發,以便將 IP/路由器上的任何特定端口請求發送到 LAN 服務器。 確保在路由器和服務器上都運行防火牆以保護其他可能暴露的端口。
我知道這可能看起來很復雜,但相信我,這是最好的解決方案。 如果您需要任何幫助 PM 我,我可以嘗試指導您解決您可能遇到的任何問題。
在不得不調試 SSE 的幾個單獨實例之一中,我發現if (ob_get_level() > 0) {ob_end_clean();}
具有諷刺意味的是導致了這個問題。 如果沒有任何級別,這就是防止 PHP 錯誤產生所需的代碼。 恢復到ob_end_clean();
解決了這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.