PHP 預設腳本執行時間上限(通常 30 秒)與輸出緩衝機制,是實現長連結(Long-lived HTTP Connection)的兩大攔路虎。set_time_limit(0) 移除時間限制,flush() 與 ob_flush() 雙重清空緩衝區,才能讓瀏覽器即時接收資料流。
<?php
header("Content-Type: text/plain; charset=utf-8");
set_time_limit(0); // 0 代表無限制
@ini_set('implicit_flush', 1); // 建議開啟隱式刷新
@ini_set('zlib.output_compression', 0); // 關閉壓縮,避免干擾
$msg = "Hello World\n";
while (true) { // 無限迴圈模擬長連結
echo $msg;
ob_flush(); // 刷新 PHP 輸出緩衝
flush(); // 刷新 Web Server 緩衝(Apache/Nginx)
sleep(5); // 模擬業務間隔
}
?>
set_time_limit(0) 的作用域php.ini 設定 max_execution_time 為 0,則全域生效。注意:在安全模式或某些虛擬主機環境下可能被禁用。ob_flush() 再 flush()?ob_flush 先推送至 SAPI 層,flush 再推送至 TCP 連結。順序不可顛倒。curl -N 或瀏覽器直接訪問。Chrome 需收到約 256 bytes 以上才開始渲染,建議每次輸出補足長度或啟用 Content-Type: text/event-stream (SSE)。tail -f 網頁版| 方案 | 優點 | 缺點 |
|---|---|---|
| PHP 原生長連結 | 零依賴、部署簡單 | 佔用 FPM 進程、難以水平擴展、易觸發 Proxy/負載均衡超時 |
| Swoole / Workerman | 常駐記憶體、高併發、支援 WebSocket | 需獨立服務、學習曲線較陡 |
| Node.js + Socket.io | 生態成熟、雙向通訊 | 技術棧分裂、維護成本高 |
| Managed 服務 | 無運維、自動擴縮容 | 成本較高、資料經第三方 |
proxy_buffering off;zlib.output_compression 或 Nginx gzip 會導致內容一次性輸出,必須關閉。gc_collect_cycles(),避免循環引用堆積。set_time_limit(0) 配合雙重 flush 是 PHP 實現伺服器推送的經典技巧,適合中小型即時場景。若併發超過百級、需雙向通訊或長期維護,強烈建議遷移至 Swoole、Workerman 或引入專用訊息推送服務。選對工具,事半功倍。