好,那我们直接 上生产级 2.0 版本 👍
我给你一个真实可落地的 Go WebSocket 架构,支持:
我会先讲 整体架构,再给你 关键代码(可运行)。
┌───────────┐
│ Browser │
└─────┬─────┘
│ WebSocket
┌─────▼─────┐
│ WS Node │ ← gorilla/websocket
│ (Go) │
│ │
│ Hub │
│ ├─ local broadcast
│ └─ Redis publish
└─────┬─────┘
│ Pub/Sub
┌─────▼─────┐
│ Redis │
└─────┬─────┘
│
┌─────▼─────┐
│ WS Node │
│ (Go) │
│ Hub │
└───────────┘
职责:
WebSocket 收到消息:
PUBLISH ws:broadcast <msg>Redis 订阅者:
import (
"context"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func newRedis() *redis.Client {
return redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
}
type Hub struct {
clients map[*Client]bool
register chan *Client
unregister chan *Client
broadcast chan []byte
redis *redis.Client
}
func newHub(rdb *redis.Client) *Hub {
return &Hub{
clients: make(map[*Client]bool),
register: make(chan *Client),
unregister: make(chan *Client),
broadcast: make(chan []byte),
redis: rdb,
}
}
func (h *Hub) run() {
for {
select {
case c := <-h.register:
h.clients[c] = true
case c := <-h.unregister:
if _, ok := h.clients[c]; ok {
delete(h.clients, c)
close(c.send)
}
case msg := <-h.broadcast:
// 1. 本地广播
for c := range h.clients {
select {
case c.send <- msg:
default:
delete(h.clients, c)
close(c.send)
}
}
// 2. 广播到 Redis
h.redis.Publish(ctx, "ws:broadcast", msg)
}
}
}
func (h *Hub) subscribeRedis() {
sub := h.redis.Subscribe(ctx, "ws:broadcast")
ch := sub.Channel()
for msg := range ch {
data := []byte(msg.Payload)
// 防止死循环:只做本地广播,不再 publish
for c := range h.clients {
select {
case c.send <- data:
default:
delete(h.clients, c)
close(c.send)
}
}
}
}
📌 重点:
Redis 消息 不要再 publish 回 Redis,否则会形成风暴。
type Client struct {
hub *Hub
conn *websocket.Conn
send chan []byte
}
readPump / writePump 不需要改逻辑,只需:
// readPump 中
c.hub.broadcast <- message
func main() {
rdb := newRedis()
hub := newHub(rdb)
go hub.run()
go hub.subscribeRedis()
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
serveWs(hub, w, r)
})
log.Println("WS server on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
你现在拥有:
✅ goroutine 级隔离
✅ 无锁并发(channel 驱动)
✅ 心跳保活
✅ 横向扩展
✅ Redis 解耦
✅ 可直接上 K8s / Docker
如果你愿意继续升级,我可以直接帮你做:
userID → conn
roomID → []conn
你直接告诉我👇
👉 你是做 IM?实时推送?语音 / LLM 流式?任务日志?
我可以直接按你的真实业务给你定制一版。