日期 | 更新说明 |
---|---|
2025年7月29日 | 初版发布 |
前言
提到服务器主动推送消息给客户端方式,大家可能第一反应想到是WebSocket
(当然你说客户端轮询也可行,只不过不是那么的优雅而已);而今天要提到的新的一种通信方式:SSE (Server-Sent Events)
。尤其在当前AI大模型爆火的年代,LLM聊天机器人几乎已成成为日常必备的工具,其背后消息的推送主要即使基于SSE (Server-Sent Events)
来实现消息推送的;如果你有兴趣让我们一起来探讨下这项技术背后的内容。
概述
是什么(What)
SSE (Server-Sent Events),服务器发送的事件: 通过 HTTP 为 Web 应用程序提供从服务器到客户端的事件流的异步通信。服务器可以向客户端发送非定向消息/事件,并且可以异步更新客户端,目前主流浏览器都支持该特性(IE 除外)。

为了便于理解,通俗一些理解,客户端和服务端建立链接以后,服务端可以向重复客户端发送数据流;可以理解成平时看视频,服务器可以提前推送给你推送流数据用于视频缓冲;只不过这个过程单工通信方式;下图方式便于理解,

为什么(Why)
既然有了WebSocket
还要推出SSE (Server-Sent Events)
呢?
SSE (Server-Sent Events)的本质是啥?有何特点,这里直接给你对比下区别:
Server-Sent Events | WebSocket | |
---|---|---|
通信模式 | 单向通道(HTTP长连接) | 双向实时通道独立协议 |
开发实现 | 简单,浏览器原生支持,服务端发送Content-Type: text/event-stream响应头 | 复杂,需在客户端/服务端分别实现握手、帧解析、心跳维持等逻辑 |
文本格式 | 文本 | 文本和二进制 |
其实简单的总结一下,如果你刚好需要服务主动推送数据的,如果追求强交互、强实时、协议定制化建议使用WebSocke
,如果当初需要服务推送数据的话使用Server-Sent Events
是个优雅的选择。
实践(How)
下面将演示服务端每个一秒中发送带时间的消息给浏览器(客户端),我们分别会使用 Java
和Rust
分别演示,先看效果:
问:为啥要rust
演示 SSE
呢?
答:作者在学 元神语言:rust


- 浏览器编码:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>SSE handler Rust</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="">
</head>
<body>
<!--[if lt IE 7]>
<p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
<![endif]-->
<h1>Server Sent Events Power By Rust</h1>
<div id="con"></div>
<script lang="javascript">
let chat = document.getElementById("con");
var source = new EventSource("/events");
source.onmessage = function(event) {
chat.innerHTML += event.data + '<br/>';
console.log("Got:", event.data);
};
</script>
</body>
</html>
- 服务端
java
SseEmitter sseEmitter = sseEmitterMap.get(uid);
if (sseEmitter == null) {
log.info("消息推送失败uid:[{}],没有创建连接,请重试。", uid);
return false;
}
try {
sseEmitter.send(SseEmitter.event().id(messageId).reconnectTime(Duration.ofSeconds(1).toMillis()).data(message));
log.info("用户{},消息id:{},推送成功:{}", uid, messageId, message);
return true;
} catch (Exception e) {
sseEmitterMap.remove(uid);
log.info("用户{},消息id:{},推送异常:{}", uid, messageId, e.getMessage());
sseEmitter.complete();
return false;
}
Java 部分只需要引入 ·SpringMvc·其实可以了
rust
TypedHeader(user_agent): TypedHeader<headers::UserAgent>,
) -> Sse<impl Stream<Item = Result<Event, Infallible>>> {
info!("`{}` connected", user_agent.as_str());
// A `Stream` that repeats an event every second
//
// You can also create streams from tokio channels using the wrappers in
// https://docs.rs/tokio-stream
let stream = stream::repeat_with(|| {
let data = format!("hi! now:{}", Local::now().to_string());
info!("send message: [{}]", data);
Event::default().data(data)
})
.map(Ok)
.throttle(Duration::from_secs(1));
Sse::new(stream).keep_alive(
axum::response::sse::KeepAlive::new()
.interval(Duration::from_secs(1))
.text("keep-alive-text"),
)
rust 需要引入
axum
rust 的 SSE 案例工程链接:github.com/will-we/blo...
Java的 SSE 案例工程链接:github.com/will-we/blo...