以下是一个完整的 Server-Sent Events (SSE) 在 Go 中的实现示例,包含服务端和客户端的代码。SSE 适用于服务器向客户端单向推送实时数据的场景(如通知、日志流等)。

Server-Sent Events (SSE)

  • 特点:单向通信(服务器→客户端),基于 HTTP/1.1 或 HTTP/2,支持自动重连。

  • 适用场景:实时通知、股票行情、新闻推送等只需服务器推送数据的场景。

  • 优点

    • 简单易用,无需额外协议升级。

    • 低延迟,适合高频推送。

    • 天然支持 HTTP/2 多路复用,减少连接开销2711。

  • 缺点:不支持客户端向服务器主动发送数据。

HTTP/2 + SSE

  • 特点:结合 HTTP/2 的多路复用能力,SSE 可高效传输服务器推送数据。

  • 适用场景:需要高效单向通信的场景(如实时监控)。

  • 优点

    • 减少 TCP 连接数,提高传输效率。

    • 兼容现有 HTTP 基础设施211。

  • 缺点:仍然不支持双向通信。

SSE Golang实现

1. 服务端代码(Go)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

// SSE 事件流处理函数
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")

	// 创建一个通道用于模拟数据更新
	eventChan := make(chan string)
	defer close(eventChan)

	// 模拟异步数据生成(实际可能是数据库监听、MQ 订阅等)
	go func() {
		for i := 1; i <= 10; i++ {
			eventChan <- fmt.Sprintf("Event %d: %s", i, time.Now().Format("15:04:05"))
			time.Sleep(2 * time.Second) // 每 2 秒推送一次
		}
	}()

	// 监听客户端断开连接
	ctx := r.Context()
	for {
		select {
		case <-ctx.Done():
			log.Println("Client disconnected")
			return
		case event := <-eventChan:
			// 写入 SSE 格式的数据(data: 开头,结尾用 \n\n)
			_, err := fmt.Fprintf(w, "data: %s\n\n", event)
			if err != nil {
				log.Println("Write error:", err)
				return
			}
			// 刷新缓冲区,确保数据立即发送
			w.(http.Flusher).Flush()
		}
	}
}

func main() {
	http.HandleFunc("/sse", sseHandler)
	http.Handle("/", http.FileServer(http.Dir("./static"))) // 静态文件服务(放客户端 HTML)

	log.Println("Server started at :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

2. 客户端代码(HTML + JavaScript)

在项目目录下创建 static/index.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo</title>
</head>
<body>
    <h1>SSE 实时数据流</h1>
    <div id="events"></div>

    <script>
        const eventSource = new EventSource("/sse");

        // 监听服务器推送的事件
        eventSource.onmessage = (e) => {
            document.getElementById("events").innerHTML += `<p>${e.data}</p>`;
        };

        // 监听错误(如连接断开)
        eventSource.onerror = (e) => {
            console.error("SSE Error:", e);
            eventSource.close();
        };
    </script>
</body>
</html>

3. 运行步骤

  1. 创建项目目录结构:
    1
    2
    3
    4
    
    your_project/
    ├── main.go
    └── static/
        └── index.html
    
  2. 启动服务端:
    1
    
    go run main.go
    
  3. 打开浏览器访问 http://localhost:8080,页面将每 2 秒显示一条服务器推送的事件。

关键点说明

  • SSE 协议格式
    每条消息以 data: 开头,结尾用两个换行符 \n\n(参考 MDN 规范)。
  • 服务端要求
    • 必须设置 Content-Type: text/event-stream
    • 使用 Flush() 立即发送数据。
  • 客户端 API
    浏览器原生支持 EventSource,自动处理重连。
  • 断开连接
    服务端通过 r.Context().Done() 检测客户端是否断开。

扩展功能

  1. 自定义事件类型
    服务端发送:
1
fmt.Fprintf(w, "event: customEvent\ndata: %s\n\n", "Custom Data")

客户端监听:

1
2
3
eventSource.addEventListener("customEvent", (e) => {
    console.log("Custom Event:", e.data);
});
  1. 重试机制
    服务端可发送 retry: 5000\n 指定客户端重连间隔(毫秒)。

如果需要处理更复杂的场景(如鉴权),可以在 URL 中添加 token 或使用 Cookie。