Golang和Vue中实现sse的创建和连接

📒 笔记 · 2024-02-12

Golang实现的后端实现sse的创建,并在Vue创建的前端中连接sse获取后端提供的信息。


什么是SSE

SSE,通常指的是服务器发送事件(Server-Sent Events),它是一种用于实现服务器向客户端推送实时数据的技术。与传统的客户端轮询或WebSocket相比,SSE 提供了一种更简单的方式来建立单向通信,使服务器能够向客户端推送数据而无需客户端发出请求。

下面是 SSE 的一些关键特点和工作原理:

  • 单向通信:SSE 是一种单向通信协议,意味着数据只能从服务器端发送到客户端,而不能反向发送。
  • 基于 HTTP:SSE 建立在 HTTP 协议之上,因此它不需要额外的协议或端口来通信。客户端使用简单的 EventSource 对象来接收来自服务器的事件。
  • 长连接:SSE 使用长连接来保持服务器与客户端之间的连接打开,这样服务器就可以在有新数据时随时将其发送到客户端。这种长连接的使用可以减少网络延迟和资源消耗,因为不需要频繁地建立和关闭连接。
  • 事件流:服务器通过发送一系列事件(event)到客户端来与之通信。每个事件可以包含一个特定的消息以及一个可选的标识符和其他元数据。
  • 自动重连:SSE 客户端具有自动重连的机制,如果连接中断,它会尝试重新连接到服务器,以便继续接收事件。
  • 简单易用:相对于 WebSockets,SSE 更加简单易用,特别适合那些只需要服务器向客户端推送数据,而不需要客户端向服务器发送数据的应用场景。

在使用 SSE 时,服务器需要发送特殊格式的数据到客户端,以便客户端能够正确解析。一般来说,这些数据格式是按照一定的规范组织的,通常包括一个 data 字段用来表示事件数据,以及可选的 event 字段用来表示事件类型,以及 id 字段用来表示事件的标识符等。

后端使用Golang实现sse的创建

本文使用Golang的Web框架Gin。

package main

import (
    "encoding/json"
    "net/http"
    "spider/webApi"
    "spider/xui"

    "time"
    "fmt"
    "github.com/gin-gonic/gin"
)

func main() {
    ginServer := gin.Default()
    // 设置跨域中间件
     ginServer.Use(corsMiddleware())

    ginServer.GET("/sse", func(ctx *gin.Context) {
        ctx.Header("Content-Type", "text/event-stream")
        ctx.Header("Cache-Control", "no-cache")
            ctx.Header("Connection", "keep-alive")

        ticker := time.NewTicker(3 * time.Second)
        defer ticker.Stop()

        for {
            select {
            case <-ticker.C:
                // 使用 xui.Xui() 函数获取 map 类型的数据
                data := webApi.Nezha()

                // 将 map 转换为 JSON 格式
                jsonData, err := json.Marshal(data)
                if err != nil {
                    fmt.Println("Error encoding JSON:", err)
                    continue
                }

                // 发送 JSON 格式的数据到客户端
                fmt.Fprintf(ctx.Writer, "data: %s\n\n", string(jsonData))
                ctx.Writer.Flush()
            case <-ctx.Writer.CloseNotify():
                fmt.Println("Client disconnected")
                return
            }
        }
    })

    // 启动 Gin 服务器
    ginServer.Run()
}

// 跨域中间件
func corsMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 允许指定的域来访问资源
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        // 允许指定的方法来访问资源
        c.Writer.Header().Set("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE")
        // 允许指定的请求头来访问资源
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin,Content-Type,Accept,User-Agent,Cookie,Authorization,X-Auth-Token,X-Requested-With")
        // 允许跨域请求时携带身份凭证(cookie)
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")

        // 处理预检请求(OPTIONS 请求)
        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }

        // 继续处理请求
        c.Next()
    }
}

代码中创建了一个sse链接,将函数webApi.Nezha()返回的数据发送到/sse中。并配置了跨域,允许跨域访问。

配置nginx

配置sse的nginx:

    location /sse {
        proxy_pass http://xxx.xxx.xxx.xxx:xxxx;
        proxy_http_version 1.1;
        proxy_set_header Connection '';
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 3600s;  # 设置较长的超时时间,确保连接保持打开状态
        proxy_set_header Content-Type text/event-stream;  # 设置正确的 Content-Type
        proxy_set_header Cache-Control no-cache;  # 禁用缓存
        proxy_set_header Access-Control-Allow-Origin *;  # 设置跨域允许,如果需要的话
    }

其中proxy_set_header也可以在后端的路由配置中设置。

Vue中使用SSE

let nezhaStatus = ref<any>([])

const getStatus = () => {
    const eventSource = new EventSource("/api/sse"); // 替换为你的服务器端点

    eventSource.onmessage = event => {
        // sse发送的是字符串,因此转为json格式。
        const jsonData = JSON.parse(event.data)
        nezhaStatus.value = jsonData.obj
    };

    eventSource.onerror = error => {
        console.error("SSE连接错误:", error);
        eventSource.close();
    }
}
TypeScript Go
Theme Jasmine by Kent Liao