You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Gorilla WebSocket向指定用户推送通知

解决方案:基于Gorilla WebSocket实现定向用户通知推送

核心问题分析

当前代码使用全局单一通道Chann,导致所有连接的WebSocket客户端都会收到通知信号,无法实现仅推送给目标用户的需求。需要维护用户ID与对应WebSocket连接的映射关系,实现精准推送。

改造步骤

1. 维护用户-连接映射

定义线程安全的映射,存储用户ID到其WebSocket连接的关联:

import (
    "sync"
    "github.com/gorilla/websocket"
)

// 全局维护用户ID到WebSocket连接的映射,加锁保证线程安全
var (
    clientConns = make(map[string]*websocket.Conn)
    connMutex   sync.RWMutex
)

2. 修改连接处理逻辑

用户建立WebSocket连接时将连接存入映射,连接关闭时从映射中移除:

func (h *notificationHandler) Connect(w http.ResponseWriter, r *http.Request) {
    id := r.URL.Query().Get("id")
    if id == "" {
        http.Error(w, "missing user id", http.StatusBadRequest)
        return
    }

    c, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Print("upgrade:", err)
        return
    }
    defer c.Close()

    // 将连接加入映射
    connMutex.Lock()
    clientConns[id] = c
    connMutex.Unlock()

    // 连接关闭时移除映射
    defer func() {
        connMutex.Lock()
        delete(clientConns, id)
        connMutex.Unlock()
    }()

    // 处理客户端消息(按需保留,若无需接收客户端消息可简化)
    for {
        _, _, err := c.ReadMessage()
        if err != nil {
            log.Println("read error:", err)
            break
        }
    }
}

3. 修改通知推送逻辑

创建新通知时,根据user_id找到对应WebSocket连接,直接推送信号:

func (h *notificationHandler) CreateNotification(w http.ResponseWriter, r *http.Request) error {
    // 解析请求参数
    var req struct {
        UserID string `json:"user_id"`
        Text   string `json:"text"`
    }
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        return err
    }

    // 插入通知到数据库
    _, err := h.db.Exec("INSERT INTO notifications (user_id, text) VALUES (?, ?)", req.UserID, req.Text)
    if err != nil {
        return err
    }

    // 定向推送通知信号
    connMutex.RLock()
    c, exists := clientConns[req.UserID]
    connMutex.RUnlock()

    if exists {
        go func() {
            signal := map[string]string{"event": "signal"}
            signalBytes, err := json.Marshal(signal)
            if err != nil {
                log.Println("marshal error:", err)
                return
            }
            if err := c.WriteMessage(websocket.TextMessage, signalBytes); err != nil {
                log.Println("write error:", err)
                // 推送失败时清理无效连接
                connMutex.Lock()
                delete(clientConns, req.UserID)
                connMutex.Unlock()
            }
        }()
    }

    // 返回成功响应
    w.WriteHeader(http.StatusCreated)
    return json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}

4. 清理冗余代码

原代码中的全局Chann和signal函数已不再需要,可直接删除。

关键注意事项

  • 线程安全:对clientConns的读写必须加锁,避免并发读写引发panic。
  • 无效连接清理:推送失败时及时移除无效连接,避免后续资源浪费。
  • 客户端重连:需在客户端实现连接断开后的自动重连逻辑,保证通知可达。
  • 多连接场景:若同一用户存在多设备连接,可根据需求选择覆盖旧连接或向所有连接推送。

内容的提问来源于stack exchange,提问作者Almaz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:30:52