欢迎,来自IP地址为:44.201.97.138 的朋友


服务器发送事件(Server-Sent Events)是一项强大的技术,可以实现从服务器到客户端的实时单向通信。

在本文中,我们将探讨如何在 Go 中实现 SSE。讨论其优点、用例并提供实际示例。最后,我们可以了解构建具有高效单向通信的实时应用程序的基础知识。

什么是 SSE

SSE 是一种 Web 技术,允许服务器通过单个 HTTP 连接将数据推送到客户端。

与 WebSockets 不同,SSE 是单向的,因此更易于实现,非常适合需要从服务器进行实时更新但不需要客户端到服务器通信的情况。

开发使用 SSE 的 Web 应用程序非常简单,只需要在服务器上编写一些代码来将事件传输到前端。但在处理传入事件时,客户端代码的工作方式几乎与 WebSockets 相同。这是一种单向连接,因此无法将事件从客户端发送到服务器。

SSE 的优势

  • 简单性:与 WebSockets 相比,SSE 更易于实现
  • 本机浏览器支持:大多数现代浏览器都支持开箱即用的 SSE
  • 自动重新连接:如果连接丢失,客户端会自动尝试重新连接
  • 高效:使用单个 HTTP 连接,减少开销

在 Go 中实现 SSE

对于此处的示例,我们将在 Go 中创建一个简单的 SSE 服务器,该服务器每秒将带有当前时间戳的数据发送到客户端。然后,客户端可以通过端口 8080 连接到服务器并接收这些消息。

对于示例可以做更复杂的事情,例如发送通知、显示进度条更新等。

package main

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

func sseHandler(w http.ResponseWriter, r *http.Request) {
    // Set http headers required for SSE
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    // You may need this locally for CORS requests
    w.Header().Set("Access-Control-Allow-Origin", "*")

    // Create a channel for client disconnection
    clientGone := r.Context().Done()

    rc := http.NewResponseController(w)
    t := time.NewTicker(time.Second)
    defer t.Stop()
    for {
        select {
        case <-clientGone:
            fmt.Println("Client disconnected")
            return
        case <-t.C:
            // Send an event to the client
            // Here we send only the "data" field, but there are few others
            _, err := fmt.Fprintf(w, "data: The time is %s\n\n", time.Now().Format(time.UnixDate))
            if err != nil {
                return
            }
            err = rc.Flush()
            if err != nil {
                return
            }
        }
    }
}

func main() {
    http.HandleFunc("/events", sseHandler)
    fmt.Println("server is running on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println(err.Error())
    }
}

SSE 实现的关键组件

事件流是一个简单的文本数据流,必须使用 UTF-8 进行编码。事件流中的消息由一对换行符分隔”\n\n”。冒号作为一行的第一个字符本质上是注释,会被忽略。

我们服务的核心代码部分是:

rc := http.NewResponseController(w)
fmt.Fprintf(w, "data: The time is %s\n\n", time.Now().Format(time.UnixDate))
// To make sure that the data is sent immediately
rc.Flush()

发送事件的服务器需要使用 MIME 类型 text/event-stream 进行响应。我们通过在此处设置响应标头来实现:

w.Header().Set("Content-Type", "text/event-stream")

你可能注意到了,我们还设置了其他几个标头。一个是保持 HTTP 连接打开,另一个是绕过跨域资源共享(CORS):

w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("Access-Control-Allow-Origin", "*")

最后一个重要部分是检测断开连接。在 Go 中,我们将在指定的通道中将其作为消息接收:

clientGone := r.Context().Done()

for {
    select {
    case <-clientGone:
        fmt.Println("Client disconnected")
        return
    }
}

收到的每条消息都包含以下字段的组合,每行一条:

  • event:标识所述事件类型的字符串
  • data:消息的数据字段
  • id:用于设置 EventSource 对象的最后一个事件 ID 值的事件 ID
  • retry:重新连接时间

客户端如何获取消息

打开浏览器,在地址栏输入”localhost:8080/events”,就可以收到服务器推送的消息,每秒显示服务器的时间:

服务器发送事件提供了一种高效而直接的方式来实现 Golang 应用程序中的实时服务器到客户端通信。通过利用 SSE,开发人员可以以最小的开销和复杂性创建响应式动态 Web 应用程序。

在构建由 SSE 驱动的应用程序时,需要考虑可扩展性、错误处理和客户端实现,以确保实现强大而高效的实时通信系统。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注