跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Golang 实现一个基于 Redis 订阅发布的聊天室

用redis.PubSub而非轮询,因其是Redis原生事件驱动模型,支持服务端主动推送、无消息时不占资源;但不保证消息可达与回溯,契合聊天室实时优先需求。 为什么用
redis.PubSub
而不是自己轮询? 轮询会浪费连接、增加延迟、放大 Redis 压力;
redis.PubSub
是 Redis 原生的事件驱动模型,客户端订阅后,服务端有消息就推,无消息不占资源。但注意:
PubSub
不保证消息可达(断连期间发布的消息直接丢弃),也不支持消息回溯——这刚好符合聊天室“实时优先、不存历史”的常见需求。 实操建议: 立即学习 “ go语言免费学习笔记(深入) ”; 每个客户端连接对应一个独立的
redis.PubSub
实例,避免多个 goroutine 并发调用
Receive()
导致 panic 务必在 goroutine 中单独启动
Listen()
循环,主逻辑不能阻塞在
Receive()
上 连接断开时,
Receive()
会返回
redis.Nil
或网络错误,需主动退出循环并关闭
PubSub
Subscribe
和
Publish
的 channel 命名怎么设计? 别用固定 channel 名(比如硬写
"chat:general"
),应支持多房间。推荐格式:
"chat:" + roomID
,其中
roomID
来自请求参数或 JWT payload,且需校验合法性(如只允许字母数字和下划线)。 实操建议: 立即学习 “ go语言免费学习笔记(深入) ”; 发布前检查
roomID
长度(建议 ≤64 字符)和字符集,防止注入恶意 channel 名(如含空格、星号或控制字符) 订阅时用
ps.Subscribe(ctx, channel)
,不要用
ps.Ping()
测试连通性——它会发到所有已订阅 channel,可能触发误通知 同一个 client 不能重复订阅同一 channel,否则
Receive()
会收到多份相同消息;可用
ps.Channel() == channel
粗略判断是否已订阅(更稳妥的做法是维护本地订阅映射) 如何安全地把用户输入转成 JSON 广播出去? 用户发来的消息是原始字符串,直接
Publish
会导致接收方无法解析结构(比如缺用户名、时间戳)。必须封装为统一 JSON 格式,且要防 XSS 和超长内容。 Redis 8.2.3 Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。 下载 实操建议: 立即学习 “ go语言免费学习笔记(深入) ”; 定义最小结构体:
type ChatMsg struct { Room string `json:"room"` User string `json:"user"` Text string `json:"text"` TS int64 `json:"ts"` }
,
TS
用
time.Now().UnixMilli()
Text
字段必须做截断(如
string([]rune(text)[:500])
),避免单条消息撑爆 Redis 单次 publish 限制(默认 512MB,但实际应 ≤1MB) 序列化用
json.Marshal
,不要拼接字符串;若失败(如含不可编码的 struct 字段),直接丢弃该消息并 log 错误,**绝不能 fallback 到 raw text** 接收端用
json.Unmarshal
解析,遇到非法 JSON 直接跳过,不 panic goroutine 泄漏和连接泄漏怎么避免? 典型场景:用户关闭网页,但服务端没收到通知,
PubSub
连接和监听 goroutine 一直挂着。Redis 默认 5 分钟超时踢出空闲连接,但 Go 侧不清理会导致 fd 耗尽。 实操建议: 立即学习 “ go语言免费学习笔记(深入) ”; HTTP handler 中启动 goroutine 处理订阅后,用
http.Request.Context()
作为取消信号,在
defer
里调用
ps.Close()
给
ps.Receive()
加超时控制:用
time.AfterFunc
或
select
配合
ctx.Done()
,避免永久阻塞 每条消息处理完后检查
ctx.Err()
,一旦
context.Canceled
就立即 break 循环并 close Redis 客户端用
redis.NewClient
,不要用
redis.NewFailoverClient
或
redis.NewClusterClient
——它们不支持 PubSub,会静默失败 真正难处理的是“半死连接”:TCP 还通,但客户端已崩溃。这时候得靠心跳+超时机制,而 Redis PubSub 本身不提供心跳。最简方案是在 HTTP long-polling 或 WebSocket 层做活跃检测,而不是指望
PubSub
自身恢复。

相关文章