Go中的Server-Sent Events:何时WebSocket是过度设计
想象一下:您正在编写一个服务器监控仪表板。数据每5秒更新一次。客户端必须接收负载下降的通知。您会选择什么?WebSocket?Long Polling?如果我告诉您有一种技术可以在普通HTTP上运行,不需要客户端额外库,并且非常适合单向数据传输,您会怎么想?
认识一下——Server-Sent Events (SSE)。这是一个内置于HTML5的协议,允许服务器通过单个HTTP连接实时向客户端发送数据。与WebSocket不同,SSE仅单向工作——从服务器到客户端。但这正是它非常适合通知、日志、股票报价以及任何服务器是数据源而客户端是被动监听者的任务的原因。
在本文中,我们将探讨如何在Go中实现Server-Sent Events。您将学习如何创建简单的流式传输、如何传递自定义事件以及如何处理客户端断开连接。让我们开始吧!
为什么Go + SSE如此强大?
Go是一种为网络应用而生的语言。它的goroutine和通道完美契合SSE模型:每个连接都是一个独立的goroutine,而通道则确保安全的数据传输。此外,Go是一种编译型语言,性能出色。您可以保持数万个打开的SSE连接而无需过多内存开销。
SSE相对于WebSocket的关键优势在于简单性。客户端使用标准的EventSource API,该API在所有现代浏览器中都能工作。无需额外库,无需框架。服务器端只是一个带有正确标头的普通HTTP处理器。
何时应该使用SSE?
- 单向传输——服务器发送数据,客户端仅接收。
- 通知和警报——监控系统、推送通知。
- 数据流式传输——日志、报价、推文、新闻。
- 自动重连——浏览器在连接中断时自动恢复连接。
Server-Sent Events协议基础
在编写代码之前,让我们先了解SSE在HTTP层面的工作原理。当客户端打开连接时,服务器发送特殊标头:
Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive之后,服务器以如下格式发送数据:
data: 消息
每条消息由两个换行符分隔。可以发送多个字段:
event: userlogindata: {"username": "alice"}
event字段定义事件类型,data字段定义其内容(通常是JSON)。还有id字段(最后事件的标识符)和retry字段(重连时间,以毫秒为单位)。
用Go编写SSE服务器
我们从最简单的开始——一个每秒发送当前时间的服务器。创建文件main.go:
package main
import ( "fmt" "net/http" "time")
func main() { http.HandleFunc("/events", sseHandler) http.ListenAndServe(":8080", nil)}
func sseHandler(w http.ResponseWriter, r *http.Request) { // 设置SSE标头 w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive")
// 创建flusher以立即发送数据 flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported", http.StatusInternalServerError) return }
// 发送事件的无限循环 for { select { case <-r.Con