SSE的重连机制

SSE (Server Send Events) 是基于HTTP的,服务端可以主动推送消息到客户端。是单向的。

应用场景:AI聊天流式响应、MCP的SSE通信

由于SSE是长连接的,那么如果发生断连了怎么办。

SSE是应用层协议,而断连通常是TCP连接断开。针对TCP连接,如果断开,那么连接状态不会被保留。并且TCP通常是操作系统维护,应用程序通常无法修改。所以要实现SSE重连,需要在应用层代码中进行状态维护,比如保存消息idx,同时给客户端推送每条消息时携带idx。这样即使断连,客户端也可以通过重新连接,并且在请求头携带这个idx,这样服务端就知道应该从哪个idx开始继续推送。

浏览器的SSE重连。

服务端:

const http = require('http');
const fs = require('fs');

// 存储客户端连接(用于模拟主动断连)
let clientResponses = [];

const server = http.createServer((req, res) => {
    if (req.url === '/') {
        // 提供前端页面
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(fs.readFileSync('index.html'));
    } else if (req.url === '/stream') {
        // SSE 流处理
        res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive'
        });

        // 记录客户端连接,用于后续主动断连
        clientResponses.push(res);
        req.on('close', () => {
            clientResponses = clientResponses.filter(r => r !== res);
            console.log('客户端连接关闭');
        });

        // 发送初始事件
        let eventId = 1;
        const interval = setInterval(() => {
            // 推送带 ID 的事件
            res.write(`id: ${eventId}\n`);
            res.write(`data: 这是第 ${eventId} 条消息\n\n`);
            console.log(`已推送事件 ID: ${eventId}`);
            eventId++;
        }, 2000);

        // 10秒后主动关闭连接(模拟服务器断连)
        setTimeout(() => {
            clearInterval(interval);
            res.end(); // 关闭当前连接
            console.log('服务器主动断开连接(模拟断连)');
        }, 10000);
    } else if (req.url === '/close-all') {
        // 手动触发所有连接关闭(可选,用于测试)
        clientResponses.forEach(res => res.end());
        clientResponses = [];
        res.end('已关闭所有连接');
    }
});

server.listen(3000, () => {
    console.log('SSE 服务器运行在 http://localhost:3000');
});

客户端:

<!DOCTYPE html>
<html>

<body>
    <h1>SSE 自动重连测试</h1>
    <div id="messages"></div>
    <script>
        // 连接 SSE 服务
        const source = new EventSource('/stream');
        const messagesDiv = document.getElementById('messages');

        // 接收事件时的处理
        source.onmessage = (event) => {
            console.log(event)
            const message = document.createElement('div');
            message.textContent = `收到消息(ID: ${event.lastEventId}): ${event.data}`;
            messagesDiv.appendChild(message);
            console.log('收到消息:', event.data, '事件ID:', event.lastEventId);
        };

        // 连接打开时
        source.onopen = () => {
            console.log('SSE 连接已打开');
        };

        // 连接错误时(包括断连)
        source.onerror = (error) => {
            console.error('SSE 连接错误:', error);
            if (source.readyState === EventSource.CLOSED) {
                console.log('连接已关闭,等待浏览器自动重连...');
            } else if (source.readyState === EventSource.CONNECTING) {
                console.log('正在尝试重连...');
            }
        };
    </script>
</body>

</html>

浏览器作为应用层实现了SSE重连,可以通过lastEventId这个字段来标识消息顺序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值