目录
SSE概念
SSE(Server-Sent Events)是一种服务器向客户端推送实时数据的技术。
SSE的特点
- 单向通信:服务器向客户端推送数据,客户端不能通过 SSE 向服务器发送数据
- 基于 HTTP:使用标准的 HTTP 协议,长连接方式
- 自动重连:客户端在连接断开时会自动重连
- 轻量级:实现简单,适合服务器向客户端推送实时数据的场景
应用场景
-
实时通知:如新闻更新、股票价格变动、天气预报等。
-
日志推送:服务器向客户端实时推送日志信息。
-
进度更新:如文件上传/下载进度、任务处理进度等。
优点
-
实现简单,兼容性好(基于 HTTP)。
-
支持自动重连,适合长时间运行的场景。
缺点
-
单向通信,客户端无法通过 SSE 向服务器发送数据。
-
不支持二进制数据,只支持文本数据。
轮询(Polling)概念
轮询特点
- 客户端主动请求:客户端定期向服务器发送请求,检查是否有新的数据
- 高延迟:数据更新的实时性取决于客户端的轮询间隔
- 资源消耗:频繁的请求会增加客户端和服务器的资源消耗
应用场景
简单,对实时性要求不高的场景,如简单的状态检查、数据更新等
优点
- 实现简单,兼容性好
- 适合对实时性要求不高的应用场景
缺点
- 高延迟,实时性差
- 频繁请求,浪费资源
Websocket概念
特点
双向通信:客户端和服务器可以实时双向通信
基于TCP:使用独立的websocket协议,性能高
低延迟:适合实时性要求很高的场景
应用场景
实时聊天:如在线客服、聊天室等
实时协作:如在线文档编辑、多人游戏等
高频数据交互:如股票交易、数据监控等
优点
- 低延迟,实时性高
- 支持双向通信
缺点
- 实现复杂,需要额外的websocket协议支持
- 对服务器资源消耗较大
SSE、轮询、websocket对比
|-----------|------------|-----------|-----|-------|------------------|
| 技术 | 通信方式 | 协议 | 实时性 | 实现复杂度 | 适用场景 |
| SSE | 单向,服务器→客户端 | HTTP | 高 | 简单 | 实时通知、进度更新、日志推送等 |
| 轮询 | 单向,客户端→服务器 | HTTP | 低 | 简单 | 简单数据更新、状态检查 |
| websocket | 双向通信 | websocket | 极高 | 复杂 | 实时聊天、在线协作、高频数据交互 |
SSE实现
在springboot项目中,实现一个简单的SSE程序,步骤如下
pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
SSE控制器
java
package com.tml.mouseDemo.controller;
import cn.hutool.core.date.DateUtil;
import com.alibaba.ttl.threadpool.TtlExecutors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class SSEController {
// 存储所有连接的客户端(线程安全)
private final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();
// 定时任务线程池
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
//使用ttl装饰线程池,是为了方便使用threadLocal
ScheduledExecutorService ttlScheduledExecutorService = TtlExecutors.getTtlScheduledExecutorService(scheduler);
/**
* 客户端连接 SSE 的入口
* sse的content-type是text/event-stream
*/
@GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handleSSE(@RequestParam String userId) {
SseEmitter emitter = new SseEmitter(60_000L); // 超时时间 60 秒
// 将新的 emitter 加入集合
emitters.add(emitter);
log.info("SSE emitter started");
// 注册完成和超时的回调(清理资源)
emitter.onCompletion(() -> {
log.info("SSE emitter stopped");
emitters.remove(emitter);
});
emitter.onTimeout(() -> {
log.info("SSE emitter timeout");
emitters.remove(emitter);
});
// 启动定时任务,向客户端推送数据
ttlScheduledExecutorService.scheduleAtFixedRate(() -> {
try {
String message = "Hello " + userId + "! Time: " + DateUtil.now();
log.info("SSE emitter sending message: {}", message);
emitter.send(message);
} catch (IOException e) {
emitter.completeWithError(e);
}
}, 0, 5, TimeUnit.SECONDS); // 每5秒发送一次
return emitter;
}
}
启动服务端后,再启动客户端
postman模拟客户端发起请求
![](https://i-blog.csdnimg.cn/direct/27e67a43bf5f4af5817ecc4a65b0998e.png)
总结
在AI时代,很多大模型的开放平台的api均支持流式返回和非流式返回,这里的流式返回,就是使用的SSE,比如kimi的开放平台
![](https://i-blog.csdnimg.cn/direct/b66a8a20b0274cdaa970df5691f860cc.png)
在实际应用中,可能还有其他场景适用服务器单向客户端推送数据,这个时候SSE就派上用场了,欢迎评论区留言,说出你们实际业务中,有哪些场景使用了SSE!