在Laravel中简单实现 Server-Sent Events (SSE)
前言
随着ChatGPT等聊天机器人和实时通信应用的流行,Server-Sent Events (SSE) 作为一种简单而有效的技术,用于实现服务器向客户端异步推送数据,变得越来越重要。
Server-Sent Events (SSE) 是一种允许服务器向客户端推送实时更新的技术,而无需客户端发起请求。这种模式非常适合实时通知、实时数据更新等场景。在 Laravel 这个强大的 PHP 框架中实现 SSE 非常直接且高效。下面,我将使用Laravel完成一个简单的 SSE 示例。
创建 SSE 路由和控制器
首先,我们需要为 SSE 创建一个路由和对应的控制器方法。
步骤 1: 添加路由
打开 routes/web.php 文件,添加一个新的路由指向我们即将创建的控制器方法:
Route::get('/sse', [\App\Http\Controllers\SseController::class, 'sendEvents'])->name('sse');
步骤 2: 创建控制器
运行以下命令来生成一个新的控制器:
php artisan make:controller SseController
接着,在新生成的 app/Http/Controllers/SseController.php 文件中添加 sendEvents 方法:
为了让 SSE 控制器响应由 Laravel 事件系统触发的消息,这里使用 Redis 的发布订阅模式(Pub/Sub)来获取事件消息。
// app/Http/Controllers/SseController.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
public function sendEvents()
{
$response = response()->stream(function () {
// 初始化响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
Redis::subscribe(['message-channel'], function ($message) {
// 当有新消息发布到'message-channel'时,此回调会被触发
$data = json_decode($message->payload, true);
echo "data: " . json_encode($data) . "\n\n";
ob_flush();
flush();
});
// 保持连接开启,以便持续监听
while (true) {
// 可以添加超时处理逻辑,避免空转
sleep(1);
}
});
return $response;
}
创建事件和监听器
步骤 1: 定义事件
首先,创建一个新的事件类。在终端运行以下命令:
php artisan make:event NewMessageEvent
这会在 app\Events 目录下创建 NewMessageEvent.php 文件。打开它,并定义事件承载的数据,比如
// app\Events\NewMessageEvent.php
namespace App\Events;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class NewMessageEvent
{
use Dispatchable, SerializesModels;
public $message;
/**
* Create a new event instance.
*
* @param string $message
*/
public function __construct(string $message)
{
$this->message = $message;
}
}
步骤 2: 创建监听器
接下来,创建一个监听器来响应这个事件。在终端执行
php artisan make:listener SendMessageToSSE --event=NewMessageEvent
编辑新生成的 app\Listeners\SendMessageToSSE.php 文件,使其在事件触发时向 SSE 连接的客户端发送消息:
// app\Listeners\SendMessageToSSE.php
namespace App\Listeners;
use App\Events\NewMessageEvent;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\DB; // 如果需要访问数据库等
class SendMessageToSSE
{
use InteractsWithQueue, SerializesModels;
/**
* Handle the event.
*
* @param \App\Events\NewMessageEvent $event
* @return void
*/
public function handle(NewMessageEvent $event)
{
// 这里可以添加逻辑判断是否需要推送消息
// 例如检查是否有客户端连接、过滤消息内容等
// 发布消息到Redis的频道,例如'messages-channel'
Redis::publish('messages-channel', json_encode([
'message' => $message,
]));
}
步骤 3: 注册事件监听器
在 app\Providers\EventServiceProvider.php 文件的 $listen 属性中注册事件监听器
protected $listen = [
NewMessageEvent::class => [
SendMessageToSSE::class,
],
];
步骤 4: 触发事件
在你的业务逻辑中,当需要推送消息时,简单地触发 NewMessageEvent 事件:
event(new NewMessageEvent('Hello from Laravel!'));
这样,每当在你的应用中触发 NewMessageEvent,通过 Redis 订阅机制,SSE 控制器就会接收到消息,并将其推送给所有已连接的客户端。
前端实现
前端使用JavaScript的EventSource来连接这个SSE流:
const eventSource = new EventSource('/sse');
eventSource.onmessage = function(event) {
console.log(event.data);
};
这样,每当服务器端发送消息时,前端的onmessage事件处理器就会被触发,并且可以在其中处理接收到的数据。