实现sse服务的关键在于设置正确的响应头并保持连接不断开。1. 设置content-type为text/event-stream;2. 设置cache-control为no-cache;3. 设置connection为keep-alive;4. 使用goroutine处理并发连接,用map管理客户端;5. 广播消息时遍历客户端map并调用flush()确保数据立即发送;6. 定期发送ping消息、清理无效连接以保持稳定;7. 注意cors跨域问题、事件id重连机制、连接超时配置及压力测试等细节。

实现服务器推送事件(SSE)服务,用Golang其实挺方便的。Golang的net/http包天然支持长连接,配合goroutine可以轻松处理大量并发连接,非常适合用来实现SSE服务。

下面我从几个实用角度讲讲怎么在Golang中搭建一个SSE服务。
SSE的基本结构
SSE是基于HTTP的协议,服务器保持连接打开,并通过特定的格式向客户端发送事件。客户端使用
EventSource对象来接收消息。
立即学习“go语言免费学习笔记(深入)”;

Golang实现SSE的关键在于:
- 设置正确的
Content-Type
为text/event-stream
- 设置
Cache-Control
为no-cache
- 保持连接不断开,持续写入数据
示例响应头:

w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")如何处理多个客户端连接
一个典型的SSE服务需要同时处理多个客户端连接。Golang的并发模型非常适合这种场景,每个连接可以由一个独立的goroutine处理。
你可以使用一个全局的客户端管理器,比如用一个
map来保存所有连接的
http.ResponseWriter,然后在有新事件时广播给所有客户端。
示例结构:
var clients = make(map[http.ResponseWriter]struct{})
var mu sync.Mutex当有新连接时,加锁添加到map中;事件发生时,遍历map发送数据。
广播函数示例:
func broadcast(data string) {
mu.Lock()
defer mu.Unlock()
for client := range clients {
fmt.Fprintf(client, "data: %s\n\n", data)
// 注意:需要flush,否则可能缓存不发送
if f, ok := client.(http.Flusher); ok {
f.Flush()
}
}
}如何保持连接稳定和优化性能
SSE连接是长连接,客户端可能会断开重连,所以服务端要处理好连接失效的情况。
建议做法:
- 定期发送
ping
消息(比如空事件)保持连接活跃 - 每次写入后调用
Flusher.Flush()
,确保数据立即发送 - 定期清理无效连接(比如在写入失败时从clients中移除)
清理无效连接的示例逻辑:
func addClient(w http.ResponseWriter) {
mu.Lock()
defer mu.Unlock()
clients[w] = struct{}{}
}
func removeClient(w http.ResponseWriter) {
mu.Lock()
defer mu.Unlock()
delete(clients, w)
}每次发送失败(比如写入时出错)就调用
removeClient。
实际使用中的一些注意事项
-
跨域问题:如果前端和后端不在同一个域,记得设置CORS头,比如
Access-Control-Allow-Origin
-
事件ID和重连机制:客户端可以通过
event.lastEventId
获取最后收到的ID,服务端可以支持从某个ID继续发送 -
连接超时设置:默认情况下,Golang的HTTP服务器可能会在一段时间后关闭空闲连接。可以通过设置
http.Server
的ReadTimeout
、WriteTimeout
来延长 - 压力测试:SSE是长连接,连接数多了会占用资源,建议做压测,看是否需要调整系统参数(如ulimit)
基本上就这些。用Golang实现SSE不复杂,但有几个细节容易忽略,比如flush、连接清理和跨域设置,把这些处理好了,就可以稳定运行了。










