Go 開發實時推送太難?試試 Sponge SSE,一鍵搞定!

各位 Gopher 們!你們是否曾遇到過這樣的場景:

如果你對以上任何一個問題點了頭,那麼恭喜你,你可能一直在用 “輪詢” 這個老辦法。這就像是你派了個小弟,每五秒鐘跑去廚房問一次:“飯好了沒?”。不僅小弟跑斷腿,廚師也煩得不行。

難道就沒有更優雅的辦法嗎?當然有!今天的主角——Server-Sent Events (SSE),就是來拯救我們的!而我們要介紹的這個 Go SSE 庫,更是能讓你 “一鍵” 擁有這項超能力!

什麼是 SSE?它和 WebSocket 有啥不一樣?

在深入代碼之前,我們先用大白話聊聊原理。

SSE (Server-Sent Events),顧名思義,就是 “服務器發送的事件”。它建立在普通的 HTTP 連接上,但這個連接是 “長連接”,而且是單向的。

把它想象成一個電臺廣播

一旦你把收音機調到正確的頻道(建立連接),電臺(服務器)就可以隨時給你播送新聞、音樂(發送數據),而你不需要每分鐘都打電話去問:“有新節目嗎?”。

那它和 WebSocket 有啥區別呢?

總的來說,如果你的需求是 “服務器 -> 客戶端” 的單向通知,那麼 SSE 就是那個更簡單、更對症的“輪子”。

這個 SSE 庫有哪些功能?

實現 SSE 的開源庫不少,很多 SSE 庫都只有基本功能,而這個 SSE 庫真的太貼心了,就像一個全能管家:

聽起來是不是很酷?別急,上代碼的感覺更酷!

三分鐘上手:搭建你的第一個 SSE 服務

讓我們用一個簡單的例子,看看用 sse 庫快速搭建一個服務有多簡單。假設我們要搭建一個每 5 秒鐘向所有客戶端廣播一句 “Hello World” 的服務。

1. 服務端代碼 (server.go)

你需要一個 Go 環境,並安裝 Gin 框架(這個例子裏用到了 Gin,當然你也可以用 Go 自帶的 net/http)。

go get github.com/gin-gonic/gin
go get github.com/go-dev-frame/sponge/pkg/sse

然後,創建 main.go 文件:

package main

import (
    "fmt"
    "net/http"
    "strconv"
    "time"
    "math/rand"
    "github.com/gin-gonic/gin"
    "github.com/go-dev-frame/sponge/pkg/sse"
)

func main() {
    // 1. 初始化我們的 SSE "廣播中心" (Hub)
    // 把它想象成那個電臺的總控制室
    hub := sse.NewHub()
    defer hub.Close()

    // 2. 用 Gin 創建一個 Web 服務器
    r := gin.Default()

    // 3. 創建一個 "/events" 接口,讓客戶端來“收聽廣播”
    r.GET("/events", func(c *gin.Context) {
        fmt.Println("新聽衆加入!")
        // 這裏爲了演示,我們給每個連接的客戶端隨機分配一個ID
        uid := strconv.Itoa(rand.Intn(999) + 1000)
        hub.Serve(c, uid)
    })

    // 4. [可選] 創建一個接口,可以手動觸發廣播
    // 你可以用 curl 命令來測試:
    // curl -X POST -H "Content-Type: application/json" -d '{"events":[{"event":"message","data":"這是一條手動廣播!"}]}' http://localhost:8080/push
    r.POST("/push", hub.PushEventHandler())

    // 5. 啓動一個不知疲倦的“播報員” (goroutine)
    go func() {
        i := 0
        for {
            // 每 5 秒鐘準備一條新消息
            time.Sleep(time.Second * 5)
            i++
            msg := "大家好,我是第 " + strconv.Itoa(i)" 條自動廣播!"
            
            // 創建一個標準事件
            event := &sse.Event{
                Event: "message", // 事件類型,可以自定義
                Data:  msg,
            }
            
            // 調用 hub.Push 進行廣播 (uid 列表傳 nil 就是廣播給所有人)
            fmt.Printf("正在廣播: %s\n", msg)
            _ = hub.Push(nil, event) 
        }
    }()

    fmt.Println("SSE 服務器已在 http://localhost:8080 啓動")
    // 啓動服務器
    if err := http.ListenAndServe(":8080", r); err != nil {
        panic(err)
    }
}

看,是不是超級清晰?初始化 Hub -> 創建連接點 -> 推送消息,搞定!

2. 客戶端代碼 (client.go)

現在,我們需要一個 “收音機” 來接收消息。這個庫同樣提供了客戶端實現,非常方便。

package main

import (
    "fmt"
    "github.com/go-dev-frame/sponge/pkg/sse"
)

func main() {
    url := "http://localhost:8080/events"

    // 1. 創建一個 SSE 客戶端,指向我們的服務器地址
    client := sse.NewClient(url)

    // 2. 註冊一個事件監聽器
    // 告訴客戶端:“一旦你收到了類型爲 'message' 的事件,就執行下面的函數”
    client.OnEvent("message", func(event *sse.Event) {
        // event.Data 就是我們從服務器收到的消息內容
        fmt.Printf("收到了新廣播!內容: 【%s】, ID: %s\n", event.Data, event.ID)
    })

    // 3. 開始連接!
    err := client.Connect()
    if err != nil {
        fmt.Printf("連接失敗了,嗚嗚嗚: %v\n", err)
        return
    }

    fmt.Println("收音機已打開,正在等待廣播... (按 Ctrl+C 退出)")
    
    // 阻塞主程序,等待客戶端退出
    <-client.Wait()
}

現在,先運行 go run server.go,然後打開另一個終端運行 go run client.go

你會看到,客戶端每隔 5 秒就會打印出一條來自服務器的新消息,完全不需要客戶端做任何多餘的操作!這就是 SSE 的魅力!

當然也可以使用其他客戶端來測試。

進階玩法:讓你的 SSE 服務更強大

SSE 庫的強大之處遠不止於此。

場景一:我不想丟失任何一條消息!

想象一下,你的服務正在推送重要的股票價格。如果客戶端因爲網絡問題斷開了 10 秒,他可能會錯過一個億!

這時,持久化存儲 和 事件重發 功能就派上用場了。

你只需要實現一個簡單的 Store 接口,告訴 sse 庫如何保存和讀取事件(比如用 Redis)。

// 僞代碼:實現一個你自己的 Store
type MyRedisStore struct{ /* ... redis client ... */ }

func (s *MyRedisStore) Save(ctx context.Context, e *sse.Event) error {
    // 把 event 序列化成 JSON 存到 Redis 的 List 或 ZSet 裏
    return nil 
}

func (s *MyRedisStore) ListByLastID(ctx context.Context, eventType string, lastID string, pageSize int) ([]*sse.Event, string, error) {
    // 根據客戶端上次收到的 lastID,從 Redis 裏查詢之後的新事件
    return events, nextLastID, nil
}

// 初始化 Hub 時,帶上你的存儲和重發配置
hub := sse.NewHub(
    sse.WithStore(&MyRedisStore{}), // 使用你的 Redis 存儲
    sse.WithEnableResendEvents(),  // 開啓斷線重發功能!
)

就這麼簡單!現在,當客戶端斷線重連時,它會自動帶上它收到的最後一條消息的 ID。服務器看到後,就會從你的 Redis 裏把所有錯過的消息一次性補發給它。一個億保住了!

場景二:我想知道消息有沒有成功推送到

有時候,你想知道推送給某個特定用戶的消息是否失敗了(比如那個用戶已經下線了)。你可以設置一個 “失敗回調函數”。

failedHandler := func(uid string, event *sse.Event) {
    // 這裏的代碼會在推送失敗時執行
    log.Printf("哎呀,給用戶 %s 推送消息 %s 失敗了!可以記錄下來,稍後重試。", uid, event.ID)
}

hub := sse.NewHub(sse.WithPushFailedHandleFn(failedHandler))

這樣,你就可以對推送失敗的事件進行記錄、告警或者其他的補償操作了。

總結

Server-Sent Events (SSE) 是構建現代實時應用的利器,尤其是在處理服務器到客戶端的單向數據流時,它比 WebSocket 更輕量、更簡單。

而 sse 這個庫,則像一個裝備精良的瑞士軍刀,不僅提供了 SSE 的核心功能,還貼心地爲你準備了持久化、斷線重連、失敗處理、性能監控等一系列 “豪華配置”。它讓開發者可以從繁瑣的連接管理和異常處理中解放出來,專注於業務邏輯的實現。

所以,下次當你的產品經理再提出 “實時更新” 的需求時,別再愁眉苦臉地去寫輪詢了。自信地拍拍胸脯,告訴他:“沒問題,分分鐘搞定!” 然後,優雅地 import "github.com/go-dev-frame/sponge/pkg/sse",開始你的表演吧!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/riYsMFcYAuWSLNs1eRHlvA