找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1538

积分

0

好友

193

主题
发表于 7 天前 | 查看: 17| 回复: 0

你有没有想过,为什么 ChatGPT 能够像人类聊天一样逐字输出回答?为什么股票软件的价格能实时跳动,而无需你手动刷新?

这背后的关键技术之一,就是 SSE (Server-Sent Events),一种让服务器能够主动向浏览器推送数据的通信方式。

一、什么是 SSE?一个通俗的比喻

我们可以用一个饭店点餐的场景来理解几种不同的通信模式:

  • 轮询 (Polling):你点了菜,然后每隔一分钟就跑过去问服务员:“我的菜好了没?”
  • SSE:你点了菜,安心坐着。菜一做好,服务员就会主动走过来告诉你:“您的菜好了,请慢用。”
  • WebSocket:你和服务员之间装了一部对讲机,你们可以随时进行双向对话。

SSE 是一种 基于 HTTP 的单向通信协议。它允许服务器在建立一次连接后,持续不断地向客户端(通常是浏览器)发送数据流。你可以把它想象成一根从服务器连接到浏览器的“数据水管”,一旦打开,服务器就能随时通过这根管子输送“水流”(数据)。

它的核心特点包括:

  • 单向通信:数据流向主要是服务器 → 浏览器,类似于广播。
  • 基于标准 HTTP:无需升级协议或使用特殊端口,兼容现有基础设施。
  • 自动重连:连接意外中断时,浏览器会自动尝试重新建立连接。
  • 轻量级:现代浏览器原生支持,无需引入额外的第三方库。
  • ⏱️ 低延迟:数据近乎实时到达,延迟在毫秒级别。

二、SSE 与其他通信技术的对比

为了更清晰地定位 SSE,我们将其与常见的技术进行对比:

技术 通信方向 协议 使用复杂度 适用场景 浏览器原生支持
轮询 (HTTP Polling) 客户端 → 服务端 HTTP 简单 消息更新不频繁的应用
长轮询 (HTTP Long Polling) 客户端 → 服务端 HTTP 中等 低频但对实时性有要求
SSE 服务端 → 客户端 HTTP 简单 消息流实时推送 ✅ (除旧版IE)
WebSocket 双向 自定义协议 复杂 聊天、游戏、IoT等强交互场景

🔍 技术选型指南

  • 选 SSE:当你只需要服务器向客户端单向推送数据时(如新闻推送、实时监控仪表盘),SSE 是最简单、最直接的选择。
  • 选 WebSocket:当应用需要双向、全双工的实时通信时(如在线聊天室、多人在线游戏),WebSocket 是更合适的工具。
  • 选轮询/长轮询:如果数据更新频率极低,且对实时性要求不高,简单的轮询反而能减少复杂度。

💡 一个精妙的比喻

  • SSE 像收音机:你只能调频接收电台的广播信号。
  • WebSocket 像电话:双方可以随时进行双向对话。
  • 轮询 像不断翻看信箱:你不知道是否有新信件,只能定期查看。
  • 长轮询 像守在信箱旁等邮差:你一直等到有新信件投递进来才离开。

三、SSE 的工作原理

下图清晰地展示了 SSE 从建立连接到持续通信,再到断线重连的完整工作流程:

SSE 通信流程示意图:浏览器发起GET请求,服务器保持连接并持续推送数据消息、事件消息和控制消息

📦 关键要素解析

  1. HTTP 请求头:客户端在发起请求时,需要在 Header 中设置 Accept: text/event-stream,明确告知服务器:“我要接收事件流格式的数据”。
  2. 响应格式:服务器响应的 Header 必须包含 Content-Type: text/event-stream,并将连接保持为打开状态,以便持续发送数据。
  3. 事件格式:服务器推送的每条消息都有固定格式,例如 data: 这是一条消息\n\n。消息以 data: 开头,并以两个换行符 \n\n 标识一条消息的结束。
  4. 自动重连:连接因网络问题断开后,浏览器会根据之前收到的信息(如Last-Event-ID)自动发起新的连接请求,实现断点续传。

四、SSE 的典型应用场景

SSE 技术因其简单高效,已在众多产品中落地应用:

1. 🤖 ChatGPT 的流式回答

ChatGPT流式回答流程:用户提问 → OpenAI服务器 → SSE流 → 浏览器逐字显示
你是否注意到,ChatGPT 的回答是一个词一个词“蹦”出来的?这正是利用了 SSE(或类似的流式传输技术)来实时推送 AI 生成的内容,极大地提升了对话的交互感和响应速度。

2. 🚨 后台系统实时提醒

例如:

  • 新订单生成通知。
  • 用户提交了新的评论或反馈。
  • 后台工单状态变更。
    这些需要即时感知的状态更新,都可以通过 SSE 推送给管理员界面,实现真正的实时提醒。

3. 📈 实时数据监控面板

  • 股票、加密货币的价格实时变动。
  • 服务器集群的 CPU、内存使用率。
  • 物联网设备的传感器数据(温湿度、GPS位置)。
    后端只需按固定频率(如每秒)推送一次数据,前端图表即可实现平滑的动态更新。

五、实战代码示例 (前端 + 后端)

前端代码 (原生 JavaScript)

使用浏览器原生的 EventSource API 即可轻松连接 SSE 服务。

<script>
// 创建 EventSource 对象,连接服务器端点
const eventSource = new EventSource('/sse-connect?userId=123');

// 监听通用的 message 事件
eventSource.onmessage = function(event) {
    console.log('收到消息:', event.data);
    // 在这里更新 DOM,将数据显示在网页上
    document.getElementById('message-box').innerHTML += event.data + '<br>';
};

// 监听特定命名的事件(对应服务器发送的 event: notification)
eventSource.addEventListener('notification', function(event) {
    console.log('收到通知事件:', event.data);
    alert('新通知:' + event.data);
});

// 监听错误事件
eventSource.onerror = function(err) {
    console.error('SSE连接出现错误', err);
    // 可以在这里给用户展示连接中断的提示
};
</script>

后端代码示例 (Spring Boot)

这里以 Java 生态中常用的 Spring Boot 框架为例,展示如何创建一个 SSE 端点。

@RestController
public class SseController {
    // 用于保存所有活跃连接的Emitter
    private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();

    // 浏览器连接入口
    @GetMapping("/sse-connect")
    public SseEmitter connect(@RequestParam String userId) {
        // 创建一个SseEmitter,设置超时时间(例如30分钟)
        SseEmitter emitter = new SseEmitter(30 * 60 * 1000L);
        emitters.put(userId, emitter);

        // 发送一条欢迎连接成功的消息
        try {
            emitter.send(SseEmitter.event()
                    .name("welcome") // 事件名称
                    .data("🎉 连接成功!欢迎使用 SSE 服务"));
        } catch (IOException e) {
            System.err.println("发送欢迎消息失败: " + e.getMessage());
        }

        // 设置完成和超时回调,清理资源
        emitter.onCompletion(() -> emitters.remove(userId));
        emitter.onTimeout(() -> emitters.remove(userId));

        return emitter;
    }

    // 模拟一个定时推送服务,每2秒向所有客户端推送一次数据
    @Scheduled(fixedRate = 2000)
    public void pushData() {
        emitters.forEach((userId, emitter) -> {
            try {
                // 模拟生成一些系统指标数据
                String json = String.format(
                        "{\"time\": \"%s\", \"value\": %.2f}",
                        LocalTime.now(), Math.random() * 100
                );

                // 构建符合SSE格式的消息并发送
                emitter.send(SseEmitter.event()
                        .id(UUID.randomUUID().toString()) // 事件ID,用于断线重连
                        .name("system-metrics") // 自定义事件类型
                        .data(json)); // 数据内容
            } catch (IOException e) {
                // 发送失败,移除此连接
                emitters.remove(userId);
            }
        });
    }
}

测试连接:
你可以使用 curl 命令来测试这个 SSE 端点:

curl --location 'localhost:18500/sse-connect?userId=1' \
--header 'Accept: text/event-stream'

实际效果:
连接建立后,你将持续收到服务器推送的 JSON 格式数据流。
SSE请求响应截图:展示浏览器通过GET请求连接SSE端点并持续接收JSON格式的系统指标数据

六、总结

SSE 如同一位高效且专注的“信息快递员”,它基于标准的 HTTP 协议,让服务器能够主动、持续地将最新数据送达浏览器端,省去了客户端反复“询问”的开销。

尽管它不具备 WebSocket 那样的双向通信能力,但正是这种“单向专注”的特性,使其在服务器向客户端推送数据的场景下显得格外简洁和高效。如果你正在使用 Spring Boot 等框架构建需要实时数据推送的后台管理系统、监控大屏或新闻资讯站,SSE 是一个非常值得考虑的轻量级解决方案。

写在最后:在 Web 开发的工具箱里,没有一种技术是万能的。根据具体的场景需求(是单向推送还是双向交互?对实时性的要求有多高?),选择最合适而非最热门的技术,才是工程师价值的体现。

拜拜~


本文旨在分享 SSE 技术的核心概念与应用,希望对你有所帮助。欢迎在 云栈社区 交流更多网络通信与后端开发技术。




上一篇:基于HTML onscrollsnapchange事件绕过Akamai与Cloudflare WAF的XSS Payload解析
下一篇:利用Groovy动态脚本解析多格式支付结果,应对复杂业务变化
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 18:25 , Processed in 0.303767 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表