尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
SSE轻松实现服务端推送,比WebSocket更简单
01 引言最近做的业务项目总是遇到到长连接、服务端向前端推送消息的功能点。由于项目中已经集成了现成的WebSocket也就是没有再重复造轮子。但是如果单独搭建WebSocket可能会繁琐能不能更加简单的SSE代替呢我们一起来试试SSE这门技术。02 简介2.1 核心概念SSEServer-Sent Events是一种基于 HTTP 协议的服务端推送技术。服务端通过持久的 HTTP 连接单向向客户端推送数据客户端通过EventSourceAPI监听并处理这些事件。主要特点• 单向通信仅服务端能发送消息• 消息仅支持文本数据• 协议是常规的HTTP 协议为什么选择SSE主要原因就是简单通常我们遇到的场景都是单项推送从服务器接收数据流。。这正是EventSource所擅长的。它还支持自动重新连接此外它是一个普通的旧的 HTTP不是一个新协议。官网地址https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events2.2 SSE vs WebSocket 对比维度SSEWebSocket通信方向单向服务端 → 客户端双向底层协议HTTP独立的 WS 协议断线重连浏览器自动重连需手动实现数据格式纯文本text/event-stream任意二进制复杂度低几行代码搞定高需要握手协议代理/防火墙友好好本质就是 HTTP一般连接数限制浏览器对同域名 SSE 连接数有限制通常 6 个无此限制适用场景通知推送、日志流、进度条、AI 流式输出聊天室、实时游戏、协同编辑选型建议如果你的场景只需要服务端向客户端单向推送优先用 SSE简单、稳定、省心。不要为了听起来高级就上WebSocke。03 SpringBoot 集成 SSEorg.springframework.web.servlet.mvc.method.annotation.SseEmitter是Spring MVC原生支持的方式最常用。是在Spring 4.2及以后就开始支持的。3.1 核心控制类Slf4j RestController RequestMapping(/sse) public class SseController { private final CopyOnWriteArrayListSseEmitter emitters new CopyOnWriteArrayList(); /** * Description: 建立连接 * * Author: ws * Date: 2026/9/17 11:38 **/ RequestMapping(value /connect, produces MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter connect(HttpServletResponse response) { response.setHeader(Access-Control-Allow-Origin, *); log.info(建立连接。。。); SseEmitter sseEmitter new SseEmitter(0L); try { sseEmitter.send(SseEmitter.event().data(连接成功)); emitters.add(sseEmitter); } catch (Exception e) { e.printStackTrace(); } return sseEmitter; } /** * Description: 推送测试 * * Author: ws * Date: 2026/9/17 17:27 **/ RequestMapping(value /push) public void push() throws Exception { for (int i 0; i 10; i) { final int index i; Thread.sleep(1000); emitters.forEach(emitter - { try { emitter.send(SseEmitter.event().data(推送数据 index)); } catch (Exception e) { log.error(推送数据失败, e); } }); log.info(推送数据 i); } } }其中connect()用来建立连接并将客户端保存到CopyOnWriteArrayList中而push()用来模拟数据推送。3.2 客户单订阅const eventSource new EventSource(http://localhost:8080/sse/connect); eventSource.addEventListener(message, (event) { console.log(收到消息:, event.data); }); eventSource.onerror (event) { console.log(连接出错浏览器会自动重连...); };3.3 测试分别通过谷歌浏览器和火狐浏览器接受服务端的消息。推送消息http://localhost:8080/sse/push到这里简单的搭建就已经完成了是不是很简单。04 使用注意事项上面完成简单的搭建功能如果用在生产商可能还会有很多问题。4.1 超时问题SseEmitter构造函数接收一个Long类型的超时时间毫秒new SseEmitter(0L) // 永不超时慎用 new SseEmitter(60000L) // 60秒超时 new SseEmitter() // 使用默认超时Spring 默认 30秒注意事项• 超时时间到了之后Spring 会自动调用complete()连接会被关闭。• 如果你的推送场景是长时间保持的如实时通知要么设0L永不超时要么自己实现心跳机制定期续命。•推荐做法设置一个合理的超时时间 客户端自动重连 服务端心跳保活。永不超时固然好用但是客户端堆积的问题如何解决这是首要考虑的问题。4.2 心跳保活机制既然选择了永不超时为什么还要使用保活机制呢这是因为长时间不发送数据连接可能被中间代理、Nginx或浏览器断开。这就需要我们定时不断给客户端发送心跳用来保持连接。下面是伪代码Scheduled(fixedRate 30000) // 每30秒 public void sendHeartbeat() { emitters.forEach(emitter - { try { emitter.send(SseEmitter.event().comment(heartbeat)); } catch (IOException e) { emitter.complete(); emitters.remove(emitter); } }); }4.3 连接数管理浏览器对同一域名的 SSE 连接数有限制HTTP/1.1 下通常为 6 个。如果你的应用打开了多个标签页连接数会很快耗尽。超过6个以上的标签页均无法建立连接。解决方案• 使用 HTTP/2连接复用不受 6 个限制• 使用共享 WorkerSharedWorker让多个标签页共享一个 SSE 连接• 后端用频道/分组管理一个连接推送多个主题的数据05 易错点5.1 忘记注册事件案例就是没有使用注册事件onCompletion、onTimeout、onError。当然案例使用的永不超时onTimeout就没有了意义。会造成什么后果客户端断开后emitter 还留在列表里每次推送都报 IOException日志刷屏内存泄漏。推荐做法SseEmitter emitter new SseEmitter(timeout); emitter.onCompletion(() - emitters.remove(emitter)); emitter.onTimeout(() - emitters.remove(emitter)); emitter.onError(e - emitters.remove(emitter)); emitters.add(emitter);5.2 emitter 管理如果在异步线程中发送数据时 emitter 已完成就会出现这样的异常IllegalStateException: ResponseBodyEmitter is already set complete主要因为在另一个线程中调用emitter.send()时连接已经被超时或客户端断开关闭了。推荐做法try { emitter.send(SseEmitter.event().data(hello)); } catch (IOException | IllegalStateException e) { emitter.complete(); emitters.remove(emitter); log.warn(推送失败已移除 emitter: {}, e.getMessage()); }5.3 produces 类型produces 类型没设置或设错会导致客户端收到数据但EventSource不触发事件或者浏览器直接拒绝连接。并不是所有的浏览器都会出现这样的问题。这个可以说是针对不同版本的浏览器的兼容性处理。推荐做法// ✅ 必须设置 produces GetMapping(value /subscribe, produces MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter subscribe() { ... }如果不设置produces text/event-streamSpring 可能返回application/json浏览器EventSource会直接报错。5.4 跨域问题前后端分离项目前端控制台报CORS错误。自己在做测试的时候也遇到了所以案例使用了response.setHeader(Access-Control-Allow-Origin, *);来解决跨域。解决跨域的方法有很对可以CrossOrigin注解、全局配置CORS或者其他方式只要解决跨域问题即可。06 小结除了上面列举的问题还有其他很多细节如鉴权、集群等场景。既然小细节那么多就没有直接造好的轮子直接使用么找了一圈下来没有找到。既然找不到那就自己造一个。后面专门建一个项目将SSE作为一个独立的推送服务再也不用去考虑那些零碎的小细节。
RELATED

相关推荐

贵州智算中心油机降噪工程商怎么筛选?附 2026 项目指标核查清单

贵州智算中心油机降噪工程商怎么筛选?附 2026 项目指标核查清单

【阿九技术速览】【阿九技术速览】贵州智算中心运营方、数据中心机电工程负责人、油机降噪项目采购决策者 > - 解决痛点 :油机安装降噪工程中“施工与验证脱节、降噪后系统可靠性无保障、本地化响应缺失”等典型问题; - 核心方案 :五维筛选框架 降…

📅 2026/10/7 8:17:19
ponytail插件与skill实战:从安装到工作流自动化

ponytail插件与skill实战:从安装到工作流自动化

1. 从“ponytail”这个热搜词说起:它到底指什么最近一段时间,“ponytail”这个词在技术社区和效率工具圈子里出现的频率明显高了起来。如果你只是把它当成一个英文单词去理解,那它确实就是“马尾辫”的意思,没什么特别的。但结合“…

📅 2026/10/7 8:17:19
pstack的thermo-nuclear-code-quality-review:终极可维护性审计详解

pstack的thermo-nuclear-code-quality-review:终极可维护性审计详解

pstack的thermo-nuclear-code-quality-review:终极可维护性审计详解 【免费下载链接】pstack-claude Claude Code, Codex, Copilot, Pi, OpenCode, Gemini, and Prime Agent versions of Potetos pstack. Rigorous agent workflows with Cursor primitives translat…

📅 2026/10/7 8:12:19
MORE NEWS

更多资讯

📰

MLA 在极长上下文下的显存带宽突围:128K 场景下的算子访存深度解析

MLA 在极长上下文下的显存带宽突围:128K 场景下的算子访存深度解析在当前大模型应用全面迈向超长上下文(Long-Context)的浪潮中,代码库全量检索、法律文书解析以及长文档推理等业务需求已将提示词长度推升至 128K 甚至 256K Token…

📰

ponytail插件开发实战:从零搭建聚合层工作流与性能调优

1. 从“ponytail”这个标题说起:它到底是什么第一次看到“ponytail”这个词,很多人脑子里蹦出来的画面是扎起来的马尾辫。但在技术圈和效率工具圈里,ponytail 早就不是发型的意思了。它是一类**轻量级、可插拔、强调“收束”与“聚合”**的工…

📰

基于语义缓存(Semantic Cache)的大模型降本:利用 Milvus 过滤 20% 高频重复请求

基于语义缓存(Semantic Cache)的大模型降本:利用 Milvus 过滤 20% 高频重复请求在传统 Web 系统的高并发优化中,几乎所有的系统架构师都会第一反应在数据库前架设一层 Redis 缓存:通过对请求 URL 或参数计算 MD5 作为 …

📰

半桥驱动芯片原理与实战:自举升压、死区时间与MOS管可靠驱动

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

ESP32云端开发新姿势:ESP-Mosaico与烧录工具v3.6.5实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

故障诊断 Agent 的上下文管理:如何在有限 Context 窗口内高效组装指标、日志与事件

故障诊断 Agent 的上下文管理:如何在有限 Context 窗口内高效组装指标、日志与事件在将大语言模型引入生产环境可观测性链路构建自动化故障诊断 Agent 时,绝大多数团队遭遇的第一道硬伤并不是模型的推理能力不足,而是上下文窗口(C…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬