Skip to content

SSE的特点、场景与技术解析

在实时交互需求日益增长的 Web 开发领域,除了常见的 HTTP、WebSocket,Server - Sent Events(SSE,服务器发送事件 )凭借其独特的单向持续推送能力,在特定场景中发挥着关键作用。本文将深入剖析 SSE,带你了解它的核心特点、适用场景以及与其他通信方式的差异。

一、SSE 是什么?

SSE 是一种基于 HTTP 协议的 Web API,允许服务器主动向客户端持续推送实时数据。从协议层面看,它利用 HTTP 长连接,让服务器能以 “流” 的形式,将更新数据不断发送给客户端,无需客户端频繁发起请求查询。

简单理解,就像你订阅了一个实时资讯频道,服务器是内容发布方,一旦有新消息(如监控系统的状态变更、股票行情波动 ),就通过这条 “专属通道” 直接推送给你,而你作为客户端只需保持接收状态即可。

二、SSE 的核心特点

(一)单向推送

SSE 聚焦于 “服务器到客户端” 的单向数据传输。这意味着客户端主要是数据接收方,服务器主动掌控数据推送节奏。比如实时监控系统中,传感器数据上传到服务器后,服务器通过 SSE 直接把最新监控指标(温度、压力等 )推给客户端展示,客户端无需反向给服务器发请求要数据,简化了交互逻辑。

(二)基于 HTTP 长连接

依托 HTTP 协议建立长连接,无需额外复杂握手流程。与普通 HTTP 短连接(请求 - 响应后断开 )不同,SSE 的长连接会持续保持,只要双方不主动关闭,服务器就能随时发数据。这既利用了 HTTP 协议的通用性(防火墙、代理通常对 HTTP 友好 ),又实现了实时性需求。

(三)自动重连机制

客户端在与服务器连接意外中断时,会自动尝试重新建立连接。这一特性保障了通信的稳定性,比如网络波动导致连接暂时断开,SSE 客户端无需手动编写重连逻辑,就能自行恢复连接,继续接收后续推送的数据。

三、SSE 的适用场景

PixPin_2025-07-19_18-13-05

(一)实时监控系统

像工厂设备监控,服务器实时采集设备运行参数(转速、温度 ),通过 SSE 持续推送给监控大屏或管理端。一旦设备参数异常,能第一时间在客户端呈现,便于运维人员及时响应。

(二)股票行情更新

金融交易平台中,股票价格、成交量等数据瞬息万变。SSE 可让服务器把最新行情数据不断推送给投资者客户端,保证用户看到近乎实时的股价波动,辅助交易决策。

(三)消息通知类应用

比如站内信、系统公告,当服务器端有新消息产生(如用户收到新评论、平台发布重要通知 ),通过 SSE 直接推送给客户端,实现消息的实时触达,提升用户体验。

四、SSE 与其他通信方式的差异

(一)对比普通 HTTP

普通 HTTP 是 “客户端主动查询,服务器被动响应” 模式,适合浏览网页、查询静态或低频更新数据场景。而 SSE 是服务器主动推送,无需客户端反复发请求,更贴合实时性需求场景。例如查询历史订单用普通 HTTP 即可,但获取实时订单状态变更就得靠 SSE 持续推送。

(二)对比 WebSocket

WebSocket 主打 “双向高频交互”,客户端和服务器可随时互发消息,像在线聊天、实时协作(多人文档编辑 )这类场景,双方需要频繁收发指令和数据,WebSocket 更擅长。但 SSE 专注单向推送,实现更简单(基于 HTTP ,无需额外协议支持 ),对于只需服务器发数据给客户端的场景,SSE 轻量化优势明显,开发成本更低。

五、SSE 的简单实现(分别以 Js和Java 为例 )

(一)服务器端(Node.js + Express )

javascript
const express = require('express');
const app = express();
// 处理 SSE 请求
app.get('/sse', (req, res) => {
  // 设置响应头,标识为 SSE 类型
  res.setHeader('Content - Type', 'text/event - stream');
  res.setHeader('Cache - Control', 'no - cache');
  res.setHeader('Connection', 'keep - alive'); 
  // 模拟持续推送数据,每隔 2 秒发一次
  const timer = setInterval(() => {
    const data = `data: ${new Date().toLocaleTimeString()}\n\n`;
    res.write(data); 
  }, 2000);
  // 连接关闭时清除定时器
  req.on('close', () => {
    clearInterval(timer);
  });
});
const port = 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
java
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

@RestController
public class SseController {

    // 模拟 SSE 推送间隔
    private static final long SEND_INTERVAL = 2; // 秒

    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter handleSse() {
        SseEmitter emitter = new SseEmitter(60_000L); // 设置超时时间

        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

        try {
            // 模拟每隔 2 秒推送一次数据
            ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
                try {
                    String data = LocalTime.now().toString();
                    emitter.send(data);
                } catch (IOException e) {
                    emitter.complete();
                }
            }, 0, SEND_INTERVAL, TimeUnit.SECONDS);

            // 客户端断开连接时取消推送任务
            emitter.onCompletion(() -> {
                future.cancel(true);
                executor.shutdown();
            });

            emitter.onTimeout(() -> {
                future.cancel(true);
                executor.shutdown();
                emitter.complete();
            });

        } catch (Exception e) {
            emitter.completeWithError(e);
        }

        return emitter;
    }
}

(二)客户端(浏览器 JavaScript )

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>SSE Demo</title>
</head>
<body>
  <div id="data">等待推送数据...</div>

  <script>
    const eventSource = new EventSource('/sse');  // Js
    const eventSource = new EventSource('http://localhost:8080/sse'); // Java

    eventSource.onmessage = function (event) {
      document.getElementById('data').innerText = `最新推送数据:${event.data}`;
    };

    eventSource.onerror = function (error) {
      console.error('SSE 连接出错:', error);
      document.getElementById('data').innerText = 'SSE 连接已断开或出错';
    };
  </script>
</body>
</html>

大概的效果:

最新推送数据:15:30:00
最新推送数据:15:30:02
最新推送数据:15:30:04
...

上述代码中,服务器端通过设置特定响应头开启 SSE 通信,定时推送当前时间;客户端用 EventSource 对象建立连接,监听 onmessage 事件接收数据并展示,简单实现了 SSE 单向推送功能。

六、总结

SSE 以其单向持续推送、基于 HTTP 长连接、自动重连等特性,在实时监控、行情更新等场景中成为高效选择。与普通 HTTP 、WebSocket 各有分工,开发者可根据实际需求(单向 / 双向、实时性强度 )灵活选用。随着实时交互场景不断丰富,SSE 凭借轻量化、针对性强的优势,会持续在 Web 实时通信领域占据一席之地,值得大家深入了解与应用 。

所有文章版权皆归博主所有,仅供学习参考。