服务器发送事件(SSE)是一种允许服务器向客户端进行实时单向数据推送的技术。作为 WebSockets 的轻量级替代方案,它在许多场景下提供了更低的网络开销与资源消耗。本文将演示如何在 Go 语言中实现 SSE,并深入探讨其与 WebSockets 的优劣对比。
什么是 SSE?
SSE 是一种基于 HTTP 的协议,它允许客户端通过一个持久连接订阅来自服务器的事件流。服务器发送的数据格式为文本,每个事件可以拥有自己的名称,数据负载可以是纯文本或 JSON。
如何实现?
下面的示例清晰地展示了使用 Go 实现 SSE 服务端和前端客户端的完整过程。核心在于正确设置 HTTP 响应头,这一原则适用于任何后端语言。
后端(Go 语言)
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
http.HandleFunc("/sse", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
messageCounter := 0
for {
someMessage := fmt.Sprintf("Message: %v", messageCounter)
eventName := "event: myCustomEvent\n"
payloadMessage := fmt.Sprintf("data: %s\n\n", someMessage)
w.Write([]byte(eventName))
w.Write([]byte(payloadMessage))
time.Sleep(1 * time.Second)
w.(http.Flusher).Flush()
messageCounter++
}
})
http.ListenAndServe(":3333", nil)
}
对路由 /sse 的处理函数设置了 SSE 必需的关键响应头:Content-Type: text/event-stream 声明了事件流内容类型,Cache-Control: no-cache 防止响应被缓存,Connection: keep-alive 则用于保持连接活跃。随后,服务器进入一个无限循环,不断生成并发送格式化的 SSE 事件。
注意:事件流格式要求使用 \n 作为换行符。event: 行定义事件类型(可选),data: 行承载有效载荷,最后由一个额外的 \n(即空行)来表示一个事件的结束。
经过格式化后,客户端接收到的数据流大致如下:
event: myCustomEvent
data: Message: 1
event: myCustomEvent
data: Message: 2
前端(HTML + JavaScript)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE</title>
</head>
<body>
<h1>SSE - Real Time Messages</h1>
<ul id="messages"></ul>
<script>
const eventSource = new EventSource('/sse');
eventSource.addEventListener('myCustomEvent', event => {
const messagesElement = document.getElementById('messages');
const li = document.createElement('li');
li.innerText = event.data;
messagesElement.appendChild(li);
console.log(event);
});
</script>
</body>
</html>
前端代码非常简洁。我们创建了一个 EventSource 对象,连接到后端的 /sse 端点,从而建立持久连接。随后,通过 addEventListener 监听服务器端定义的 myCustomEvent 事件。每当该事件被触发,前端就会从 event.data 中获取消息内容,并将其动态添加到页面的列表中。
图1:SSE实时消息在前端页面的展示效果

图2:浏览器开发者工具中查看SSE事件流连接

SSE 的优点和缺点
优点:
- 基于HTTP:使用标准HTTP/HTTPS端口(80/443),易于穿透防火墙和代理服务器,兼容性极佳。
- 轻量高效:相比于WebSocket,网络开销和服务器资源占用更少,因为连接建立后主要是服务器向客户端的单向数据推送。
- 实现简单:直接利用现有HTTP协议栈,编程模型对于开发者更为熟悉。
缺点:
- 单向通信:只能由服务器向客户端推送数据,客户端无法通过同一连接向服务器发送信息。
- 浏览器支持:虽然主流现代浏览器都支持,但在某些老旧或特定平台上的支持度可能不如WebSocket完善。
- 协议限制:作为HTTP的上层功能,无法利用WebSocket协议级别的特性,如二进制数据传输、更高效的压缩和多路复用。
SSE 与 WebSockets 的比较
SSE 和 WebSockets 都用于实现实时通信,但定位不同。
WebSockets 提供了全双工通信通道,客户端和服务器可以随时相互发送数据。这使得它非常适合需要频繁双向交互的应用,例如在线聊天室、协同编辑、多人在线游戏等。
SSE 是服务器到客户端的单向通道。它完美契合了服务器主动向客户端推送更新,而客户端无需上行的场景。典型的应用包括实时新闻推送、股票价格变动、社交媒体动态通知、服务器日志流式输出等。
此外,WebSocket 是一个独立的、在TCP之上实现的协议,支持二进制帧和自定义扩展。而 SSE 是纯文本的,基于 HTTP 协议,其简单性既是优势也是限制。
选择哪种技术取决于你的具体需求:如果需要双向、低延迟、任意格式数据的自由交换,WebSocket 是首选;如果业务模型主要是服务器向客户端广播或推送文本信息,那么更简单、对服务器压力更小的 SSE 可能是更优解。欢迎在云栈社区继续探讨实时通信技术的更多细节。