Go WebSocket 实战:用 gorilla/websocket 搭一个带心跳与广播的推送服务

test12026-10-151 次阅读

WebSocket 适合什么场景

HTTP 一问一答适合取数据,但聊天、行情推送、协同编辑这类"服务端主动推"的场景,用 WebSocket 更省连接。gorilla/websocket 是 Go 里最成熟的实现,下面写一个能跑的回声与广播服务。

握手与连接升级

package main

import (
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

func serve(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("upgrade:", err)
        return
    }
    defer conn.Close()
    for {
        mt, msg, err := conn.ReadMessage()
        if err != nil {
            break
        }
        conn.WriteMessage(mt, msg) // 回声
    }
}

加心跳,挡掉死连接

客户端掉线时 TCP 不会立刻通知服务端,靠 ping/pong 定时探活:

func pump(conn *websocket.Conn, done chan<- struct{}) {
    conn.SetReadDeadline(time.Now().Add(60 * time.Second))
    conn.SetPongHandler(func(string) error {
        conn.SetReadDeadline(time.Now().Add(60 * time.Second))
        return nil
    })
    for {
        if _, _, err := conn.ReadMessage(); err != nil {
            done <- struct{}{}
            return
        }
    }
}

// 定时发 ping
ticker := time.NewTicker(30 * time.Second)
go func() {
    for range ticker.C {
        conn.WriteMessage(websocket.PingMessage, nil)
    }
}()

用 Hub 做多连接广播

type Hub struct {
    clients map[*websocket.Conn]bool
    mu      sync.Mutex
}

func (h *Hub) broadcast(msg []byte) {
    h.mu.Lock()
    defer h.mu.Unlock()
    for c := range h.clients {
        if err := c.WriteMessage(websocket.TextMessage, msg); err != nil {
            c.Close()
            delete(h.clients, c)
        }
    }
}

上线前要补的

  • 读消息加大小上限 SetReadLimit,防止超大帧打爆内存。
  • 写操作加锁或用单独的写协程,WebSocket 并发写不是安全的。
  • 反向代理(Nginx)要调大 proxy_read_timeout,否则长连被掐。
T

test1

文章作者

WebSocket 适合什么场景 HTTP 一问一答适合取数据,但聊天、行情推送、协同编辑这类"服务端主动推"的场景,...

分类
技术
发布时间
2026-10-15
字数
约 1747 字
阅读
1 次