本文目录导读:

在 Laravel 中实现 SSE (Server-Sent Events) 响应,需要合理设置响应头并保持连接持久化,以下是完整的实现方案:
路由配置
// routes/web.php 或 routes/api.php
Route::get('/sse/events', [SSEController::class, 'stream']);
控制器实现
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
class SSEController extends Controller
{
public function stream(Request $request)
{
// 设置 SSE 响应头
$response = new StreamedResponse(function () {
// 禁用输出缓冲
if (ob_get_level()) {
ob_end_clean();
}
// 设置无限执行时间
set_time_limit(0);
// 发送初始数据
$this->sendSSEData('connected', [
'message' => '连接成功',
'timestamp' => now()->toIso8601String()
]);
// 循环发送数据
while (true) {
// 检查客户端是否断开连接
if (connection_aborted()) {
break;
}
// 发送事件数据
$this->sendSSEData('message', [
'time' => now()->toIso8601String(),
'data' => 'Some real-time data'
]);
// 发送注释保持连接
echo ": keepalive\n\n";
flush();
// 等待一段时间
sleep(2);
}
});
// 设置响应头
$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('Cache-Control', 'no-cache');
$response->headers->set('Connection', 'keep-alive');
$response->headers->set('X-Accel-Buffering', 'no'); // Nginx 特定
return $response;
}
private function sendSSEData($event, $data)
{
echo "event: {$event}\n";
echo "data: " . json_encode($data) . "\n\n";
flush();
}
}
完整的高级实现
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;
class AdvancedSSEController extends Controller
{
public function stream(Request $request)
{
return response()->stream(function () use ($request) {
// 初始化
$this->initializeStream();
$lastEventId = $request->header('Last-Event-ID', 0);
$userId = $request->get('user_id');
// 发送重连ID
echo "retry: 3000\n\n";
flush();
while (true) {
if (connection_aborted()) break;
// 获取新数据
$events = $this->getNewEvents($lastEventId, $userId);
foreach ($events as $event) {
// 发送事件ID用于断线重连
echo "id: {$event['id']}\n";
echo "event: {$event['type']}\n";
echo "data: " . json_encode($event['data']) . "\n\n";
flush();
$lastEventId = $event['id'];
}
// 定时心跳
echo ": heartbeat\n\n";
flush();
// 控制频率
sleep(3);
}
}, 200, [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
'X-Accel-Buffering' => 'no',
]);
}
private function initializeStream()
{
// 关闭输出缓冲
while (ob_get_level() > 0) {
ob_end_flush();
}
set_time_limit(0);
ignore_user_abort(false);
}
private function getNewEvents($lastEventId, $userId)
{
// 从数据库获取新事件
// 示例实现
$events = [];
$newEvents = cache()->get("sse_events_{$userId}", []);
foreach ($newEvents as $event) {
if ($event['id'] > $lastEventId) {
$events[] = $event;
}
}
return $events;
}
}
前端 JavaScript 实现
class SSEConnection {
constructor(url) {
this.url = url;
this.eventSource = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.lastEventId = 0;
}
connect() {
this.eventSource = new EventSource(this.url);
// 连接打开
this.eventSource.onopen = (event) => {
console.log('SSE 连接已建立');
this.reconnectAttempts = 0;
};
// 接收消息
this.eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handleMessage(data);
this.lastEventId = event.lastEventId || 0;
};
// 自定义事件
this.eventSource.addEventListener('connected', (event) => {
console.log('连接成功:', JSON.parse(event.data));
});
this.eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
this.updateUI(data);
});
// 错误处理
this.eventSource.onerror = (error) => {
console.error('SSE 错误:', error);
this.handleReconnection();
};
}
handleReconnection() {
this.eventSource.close();
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
console.log(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
setTimeout(() => {
this.connect();
}, delay);
} else {
console.log('达到最大重连次数');
this.emitError('连接失败');
}
}
disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
}
}
// 使用示例
const sse = new SSEConnection('/sse/events?user_id=123');
sse.connect();
// 页面卸载时断开连接
window.addEventListener('beforeunload', () => {
sse.disconnect();
});
Nginx 配置优化
# 确保 Nginx 不缓冲 SSE 响应
location /sse/ {
proxy_pass http://your-app;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
# 禁用缓冲
fastcgi_buffering off;
proxy_set_header Connection '';
chunked_transfer_encoding on;
# 保持连接
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
注意事项
-
性能优化:
- 使用队列系统处理事件(Redis、RabbitMQ)
- 避免在循环中查询数据库
- 考虑使用
yield和生成器
-
错误处理:
try { while (true) { // 发送数据 } } catch (\Exception $e) { Log::error('SSE Error: ' . $e->getMessage()); } finally { flush(); } -
资源清理:
register_shutdown_function(function () { // 清理资源 cache()->forget('sse_connection_' . $userId); });
这样就可以在 Laravel 中正确实现 SSE 响应了,如果需要更复杂的事件处理,建议结合 Laravel 的事件系统和队列来管理。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播