Laravel Server Sent Events (SSE) 服务端内容无法推送至客户端问题求助
问题原因与解决方案
核心问题1:控制器属性无法跨请求共享
- PHP HTTP请求是完全隔离的生命周期,每个请求都会重新初始化控制器实例,sendContent请求中赋值的
$this->data,和EventSource发起的getData请求的控制器实例毫无关联,数据无法互通。哪怕改为静态属性,普通FPM模式下请求结束后所有资源都会销毁,依然无法跨请求留存数据。
核心问题2:SSE长连接逻辑错误
- SSE需要服务端保持连接持续流式输出,你当前的getData方法直接返回单次响应就断开连接,EventSource只会拿到一次空数据,之后进入自动重连循环,永远接收不到后续更新的推送。
核心问题3:客户端解析逻辑不匹配
- 客户端代码用
JSON.parse(event.data)解析推送内容,但服务端直接返回的是原始字符串,没有做JSON序列化,解析失败会抛出JS错误,直接中断onmessage回调执行。
修复方案
1. 服务端Laravel代码修改
首先使用跨请求存储(示例用Laravel缓存,也可替换为Redis、数据库等),改造SSE接口保持长连接循环推送:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; use Illuminate\Http\Request; class MiscController extends Controller { public function sendContent(Request $request) { $content = $request['content']; if (!is_null($content)) { // 存储推送内容,带上版本号方便客户端判断更新 $cachedData = Cache::get('sse_push_data', ['version' => 0, 'content' => '']); $cachedData['version']++; $cachedData['content'] = $content; Cache::put('sse_push_data', $cachedData); } return response()->json(['status' => 'ok']); } public function getData() { // 关闭执行超时 set_time_limit(0); // 关闭输出缓冲 ob_implicit_flush(true); ob_end_flush(); $response = response()->stream(function () { $lastVersion = 0; while (true) { $cachedData = Cache::get('sse_push_data', ['version' => 0, 'content' => '']); // 有更新才推送 if ($cachedData['version'] > $lastVersion) { $lastVersion = $cachedData['version']; // 输出SSE格式,JSON序列化内容匹配客户端解析逻辑 echo "data: " . json_encode($cachedData['content']) . "\n\n"; } // 每2秒检查一次更新,可自行调整间隔 sleep(2); } }, 200, [ 'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive', 'X-Accel-Buffering' => 'no' // 禁用Nginx反向代理缓冲,很重要 ]); return $response; } }
2. 客户端代码优化
添加错误监听方便排查,同时补充异常处理逻辑:
$(document).ready(function($) { var lastMsg = ''; const source = new EventSource("/misc/getData"); source.onmessage = function(event) { console.log('收到推送:', event.data); var data = JSON.parse(event.data); if (data !== lastMsg) { $('#result').empty(); $('#result').append('Result: ' + data); lastMsg = data; } }; // 添加错误监听排查问题 source.onerror = function(error) { console.error('SSE连接错误:', error); }; });
额外注意事项
- 如果用Nginx做反向代理,必须加
X-Accel-Buffering: no头,否则Nginx会缓冲输出内容,不会实时推送到客户端 - FPM模式下长连接资源占用较高,并发量较大的场景建议改用Swoole、Workerman等常驻进程框架实现SSE推送
- 可以给SSE推送加id字段,方便连接断开后客户端从断点续传
内容的提问来源于stack exchange,提问作者jgarcias
相关产品推荐
相关产品推荐

