Skip to content

Server-Sent Events (SSE) 是一种在客户端和服务器之间建立单向、持久连接的技术,允许服务器推送实时更新到浏览器。SSE 主要用于向浏览器推送事件(如实时通知、消息、更新等),与 WebSocket 不同,SSE 是单向的:数据从服务器流向客户端,而 WebSocket 允许双向通信。

在 Go 中实现 SSE 可以通过标准的 net/http 包来完成。下面是如何在 Go 中实现 SSE 的基本步骤。

1. SSE 的基本原理

SSE 使用 HTTP 协议,并通过特定的 Content-Type 响应头来指定消息流格式:

  • Content-Type: text/event-stream,告诉浏览器该响应是一个 SSE 流。
  • 事件数据: 每条消息以特定的格式(包括 data 字段)发送,支持自定义事件类型。

每条消息的格式通常如下:

data: <消息内容>\n\n

你可以使用其他字段,比如:

  • event: 指定事件类型。
  • id: 指定事件 ID。
  • retry: 指定客户端重试的时间间隔。

2. 在 Go 中实现 SSE 服务器

示例:创建一个 SSE 服务器

  1. 设置 HTTP 服务器:Go 中可以使用 http 包来处理 HTTP 请求。

  2. 编写 SSE 处理函数:该函数将设置必要的 HTTP 头部并持续发送事件。

完整的 Go SSE 示例

go
package main

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

// 启动一个 SSE 流
func sseHandler(w http.ResponseWriter, r *http.Request) {
	// 设置 HTTP 头部,指定响应类型为 SSE
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("Connection", "keep-alive")

	// 持续发送数据
	for {
		// 发送消息,每 1 秒发送一次
		message := fmt.Sprintf("data: %s\n\n", time.Now().Format(time.RFC3339))
		_, err := w.Write([]byte(message))
		if err != nil {
			log.Println("Error writing to client:", err)
			break
		}

		// 刷新缓冲区
		flusher, ok := w.(http.Flusher)
		if ok {
			flusher.Flush()
		}

		// 每 1 秒发送一次数据
		time.Sleep(1 * time.Second)
	}
}

func main() {
	// 设置 SSE 路由
	http.HandleFunc("/events", sseHandler)

	// 启动 HTTP 服务器
	log.Println("SSE server started on :8080")
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("Error starting server:", err)
	}
}

关键点说明:

  • 设置 Content-Type:设置 Content-Typetext/event-stream,指示浏览器这是一个 SSE 流。
  • Cache-Control: no-cache:防止缓存,以确保事件流实时更新。
  • Connection: keep-alive:保持 HTTP 连接活跃。
  • http.Flusher:确保数据被及时推送到客户端,而不是缓存在服务器端。Flusher 接口用于强制刷新 HTTP 响应流。
  • 每秒推送数据:使用 time.Sleep 每秒推送一次当前的时间。

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

客户端代码需要通过 JavaScript 使用 EventSource API 来接收从服务器推送的事件。

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE Demo</title>
</head>
<body>
    <h1>SSE Demo</h1>
    <div id="messages"></div>

    <script type="text/javascript">
        // 创建一个 EventSource 实例,连接到 SSE 服务器
        const eventSource = new EventSource('/events');

        // 监听 `message` 事件
        eventSource.onmessage = function(event) {
            // 获取数据并显示
            const message = document.createElement('p');
            message.textContent = `Received: ${event.data}`;
            document.getElementById('messages').appendChild(message);
        };

        // 监听错误事件
        eventSource.onerror = function(event) {
            console.error("Error occurred:", event);
        };
    </script>
</body>
</html>

关键点说明:

  • EventSource:这是用于接收服务器推送事件的原生 JavaScript API。
  • onmessage 事件:监听从服务器接收到的消息,并将其显示在 HTML 页面上。
  • onerror 事件:监听可能发生的错误,例如与服务器的连接丢失。

4. 测试 SSE

  1. 启动 Go 服务器:

    bash
    go run server.go
  2. 打开浏览器并访问 http://localhost:8080,你会看到页面上每秒更新当前时间。

5. SSE 的注意事项

  • 单向通信:SSE 仅支持从服务器到客户端的单向数据流。如果需要双向通信,可以使用 WebSocket。
  • 连接保持:SSE 是通过长连接实现的,浏览器会一直保持与服务器的连接,直到关闭或断开。
  • 浏览器兼容性:大多数现代浏览器支持 SSE,但老版本的浏览器可能不支持,尤其是 Internet Explorer。你可以使用 polyfill 或者其他方法来处理不兼容的情况。
  • 重连机制:浏览器会自动处理与服务器的连接断开问题,并尝试重新连接(默认是 3 秒重连),可以通过 retry 字段来控制重试间隔。
  • 最大连接数限制:由于 SSE 使用长连接,多个 SSE 流会消耗大量的资源。在大规模应用时,需要考虑负载均衡和连接数限制。

6. 总结

SSE 是一种简单而高效的技术,适用于单向数据流的应用场景,比如实时通知、消息推送、监控仪表板等。在 Go 中实现 SSE 相对简单,只需设置 HTTP 头并持续发送事件。通过结合客户端的 EventSource API,你可以轻松实现实时数据更新的功能。