Laravel+Vue SPA中SSE阻塞其他HTTP请求的解决方法
解决Laravel SSE长连接阻塞HTTP请求的问题
问题根源
Laravel默认的会话机制会在请求启动时锁定会话存储(比如文件),直到请求结束才释放锁。SSE是持续保持的长连接,会一直占用这个锁,导致后续所有需要会话的HTTP请求都被阻塞,处于等待锁释放的状态,表现为请求冻结无法完成。
解决方案
1. 手动释放SSE路由的会话锁
在SSE控制器方法的最开始,手动保存并释放会话锁,让后续请求可以正常获取会话资源。这是最直接有效的方案,适用于SSE连接不需要持续持有会话的场景。
示例代码:
use Illuminate\Support\Facades\Session; public function sse() { // 保存当前会话数据并释放锁 Session::save(); // 也可以用辅助函数:session()->save(); return response()->stream(function () { while (true) { // 检查新评论逻辑 $newComments = \App\Models\Comment::where('ticket_id', request()->ticket_id) ->where('created_at', '>', now()->subSeconds(3)) ->get(); if ($newComments->isNotEmpty()) { echo "data: " . json_encode($newComments) . "\n\n"; ob_flush(); flush(); } // 休眠降低CPU占用 sleep(2); // 检测客户端断开连接,终止循环 if (connection_aborted()) { break; } } }, 200, [ 'Content-Type' => 'text/event-stream', 'Cache-Control' => 'no-cache', 'Connection' => 'keep-alive', ]); }
2. 给SSE路由禁用会话中间件
如果SSE连接完全不需要访问会话数据(比如身份验证已通过令牌等其他方式处理),可以直接移除该路由的会话相关中间件,从根源避免锁占用。
示例路由定义:
// 移除包含会话逻辑的web中间件,以及会话专属中间件 Route::get('/tickets/{ticket}/comments/sse', [CommentController::class, 'sse']) ->withoutMiddleware(['web', 'auth.session']);
注意:若需验证用户身份,可保留
auth中间件但移除auth.session,或改用API令牌验证替代会话验证。
3. 优化会话驱动(辅助方案)
如果使用默认的file会话驱动,文件锁的阻塞问题会更明显。可以将会话驱动切换为redis或database,这类驱动的锁机制更高效,能减少长连接带来的阻塞影响。修改.env文件:
SESSION_DRIVER=redis
注意事项
- 若SSE连接过程中需要再次访问会话数据,需重新调用
Session::start()获取会话,但尽量避免这种场景,保持SSE连接轻量化。 - 务必在循环中加入休眠(
sleep()),避免服务器CPU占用过高。 - 检测
connection_aborted()确保客户端断开连接时终止循环,及时释放服务器资源。
内容的提问来源于stack exchange,提问作者Church
相关产品推荐
相关产品推荐

