SSE服务端事件单向推送

wen java案例 3

本文目录导读:

SSE服务端事件单向推送

  1. SSE 的核心特点
  2. SSE 的工作原理
  3. 完整实现示例
  4. SSE 的优缺点
  5. SSE vs WebSocket 对比
  6. 典型应用场景
  7. 注意事项
  8. 高级用法:使用 EventID 实现断线重连

SSE (Server-Sent Events) 是一种允许服务器主动向客户端推送数据的技术,与 WebSocket 不同,SSE 是单向的(服务器到客户端),且基于 HTTP 协议。

以下是 SSE 的核心机制、实现示例及适用场景:

SSE 的核心特点

  1. 单向通信:数据只能从服务器流向客户端。
  2. 基于 HTTP:使用标准的 HTTP 协议,无需复杂的握手(如 WebSocket)。
  3. 自动重连:如果连接断开,浏览器会自动尝试重新连接(默认重试间隔 3 秒)。
  4. 文本传输:只能发送文本数据(UTF-8 编码),如果需要发送二进制数据,需要先编码为 Base64 或其他文本格式。
  5. 简单易用:客户端使用 EventSource API,服务端返回 text/event-stream 格式的响应。

SSE 的工作原理

  1. 客户端请求:浏览器发起一个 GET 请求,并声明 Accept: text/event-stream
  2. 服务端响应:服务端设置响应头 Content-Type: text/event-stream,并保持连接打开(不关闭)。
  3. 数据推送:服务端定期或不定期地向客户端发送格式化的文本消息。
  4. 客户端接收:浏览器通过 EventSource 对象监听 message 或自定义事件。

完整实现示例

客户端(HTML + JavaScript)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">SSE 示例</title>
</head>
<body>
    <h1>服务端推送消息</h1>
    <div id="messages"></div>
    <script>
        // 创建 EventSource 对象,连接到服务端 SSE 端点
        // 注意:如果跨域,需要设置 withCredentials: true 并服务端允许
        const eventSource = new EventSource('/events');
        // 监听默认的 message 事件(没有指定事件名称的消息)
        eventSource.onmessage = function(event) {
            const messagesDiv = document.getElementById('messages');
            const newMessage = document.createElement('p');
            newMessage.textContent = `收到消息: ${event.data}`;
            messagesDiv.appendChild(newMessage);
        };
        // 监听自定义事件,"customEvent"
        eventSource.addEventListener('customEvent', function(event) {
            console.log('自定义事件:', event.data);
        });
        // 监听连接打开
        eventSource.onopen = function() {
            console.log('SSE 连接已建立');
        };
        // 监听错误(包括连接断开)
        eventSource.onerror = function() {
            console.error('SSE 连接出错');
            // EventSource 会自动重连
        };
        // 关闭连接(例如页面离开时的清理)
        // window.onbeforeunload = function() {
        //     eventSource.close();
        // };
    </script>
</body>
</html>

服务端(Node.js + Express 示例)

const express = require('express');
const app = express();
const PORT = 3000;
app.get('/events', (req, res) => {
    // 设置 SSE 必需的响应头
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Access-Control-Allow-Origin': '*' // 允许跨域
    });
    // 发送初始连接成功消息
    res.write('data: 已连接\n\n');
    // 每 2 秒发送一条消息
    const intervalId = setInterval(() => {
        const data = new Date().toLocaleTimeString();
        res.write(`data: ${data}\n\n`);
        // res.flushHeaders(); // 如果需要立即刷新缓冲区(某些框架需要)
    }, 2000);
    // 当客户端断开连接时,清理定时器
    req.on('close', () => {
        clearInterval(intervalId);
        console.log('客户端断开连接');
        res.end();
    });
});
app.listen(PORT, () => {
    console.log(`SSE 服务运行在 http://localhost:${PORT}`);
});

SSE 消息格式标准

服务端发送的消息行必须以 text/event-stream 格式,每条消息由多个字段组成:

field: value\n

常见字段:

  • data(必须,可以有多行)。
  • event:事件名称(可选,客户端 addEventListener 监听)。
  • id:消息 ID(可选,客户端自动设置 Last-Event-ID 以实现断线重连)。
  • retry:重连时间(毫秒,可选)。

消息之间用两个换行符 \n\n 分隔。

示例

event: userLogin
id: 12345
data: {"username": "张三", "time": "14:30:00"}

SSE 的优缺点

优点 缺点
简单易用:客户端只需 EventSource API。 单向通信:只能服务端推送,客户端无法发送数据(需结合 AJAX)。
自动重连:内置断线重连机制。 连接数限制:浏览器限制每个域名最多 6-8 个 SSE 连接。
HTTP 兼容:走标准 HTTP 端口 80/443,防火墙友好。 文本传输:不支持二进制数据(除非编码)。
实时性较好:延迟一般在 1-3 秒内。 资源消耗:服务端需要保持大量长连接(可以使用 HTTP/2 提升效率)。

SSE vs WebSocket 对比

特性 SSE WebSocket
通信方向 单向(服务端 → 客户端) 双向(全双工)
协议 HTTP WebSocket(非 HTTP)
复杂性 简单 较复杂
自动重连 内置 需手动实现
二进制数据 需编码 原生支持
浏览器支持 良好(除 IE) 广泛
应用场景 实时通知、日志流、股票价格 聊天、游戏、协同编辑

典型应用场景

  1. 实时通知系统:新订单提醒、系统告警。
  2. 股票/加密货币价格更新:低延迟推送行情。
  3. 日志流:持续推送服务器日志到前端。
  4. 社交媒体时间线:新帖子、新消息推送。
  5. 进度更新:文件上传/下载进度、任务执行状态。

注意事项

  1. 跨域问题:如果服务端和客户端不同域,需在服务端设置 Access-Control-Allow-Origin
  2. 负载均衡:SSE 是长连接,负载均衡器需要配置 sticky session(会话保持),或使用 Redis 等中间件共享状态。
  3. 资源释放:服务端务必监听 req.on('close') 事件,清理定时器和内存。
  4. 浏览器兼容性:IE 不支持,推荐使用 EventSource Polyfill 或降级方案(如轮询)。

高级用法:使用 EventID 实现断线重连

// 服务端发送带 ID 的消息
res.write(`id: ${Date.now()}\ndata: 消息内容\n\n`);
// 客户端自动携带 Last-Event-ID 重连
// 浏览器内部自动处理,无需额外代码

如果需要在重连时从最后一条消息开始,服务端可以读取请求头 Last-Event-ID

const lastId = req.headers['last-event-id'];
// 从 lastId 之后继续推送

SSE 是实现服务端单向推送最简单、最轻量的方案,如果你的需求只是服务端主动推送数据给客户端,且不需要客户端频繁发送数据回服务端(比如实时通知、新闻推送、系统状态更新),SSE 是比 WebSocket 更合适的选择。

抱歉,评论功能暂时关闭!