尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Golang Redis 发布订阅(Pub/Sub)

Golang Redis 发布订阅(Pub/Sub) Redis 发布订阅Pub/Sub一、Pub/Sub 是什么想象一下订单服务刚创建了一笔订单库存系统要减库存、通知服务要发短信、分析平台要记日志——如果每个服务之间都直接调 HTTP当第四个消费者加入时所有上游都得改代码。这种网状调用会随着系统变大而崩溃。发布订阅Pub/Sub翻转了这种模型Publisher ──(msg)── Redis ──(fan-out)── Subscriber A ── Subscriber B ── Subscriber C核心思想发布者只管往频道扔消息不关心谁在听。订阅者只管从频道收消息不关心谁发的。Redis 作为中间人负责路由。二、Redis Pub/Sub 的三个核心命令命令作用PUBLISH channel message向频道发送消息返回收到消息的订阅者数量SUBSCRIBE channel [channel ...]订阅一个或多个频道精确匹配PSUBSCRIBE pattern订阅匹配模式的所有频道如order:*一个关键性质消息不落地。Redis 不存储已发送的消息——如果订阅者掉线了错过就是错过了。这是 Pub/Sub 和消息队列RabbitMQ/Kafka的根本区别。三、Go 中的订阅与发布订阅者// 订阅者开启一个专用长连接持续接收消息pubsub:rdb.Subscribe(ctx,orders:new,payments:done)deferpubsub.Close()// Channel() 返回一个 Go channel把 Redis 的网络流转换为本地 channelch:pubsub.Channel()// 循环读消息阻塞直到来消息或 ctx 取消formsg:rangech{fmt.Printf([%s] %s\n,msg.Channel,msg.Payload)}关键细节Subscribe()后 Redis 客户端会分配一条专用 TCP 连接这条连接被锁定在 Pub/Sub 模式不能再执行普通命令GET/SET 之类的。这也是为什么生产环境通常给 Pub/Sub 单独配一个连接池。发布者// 发布一个 JSON 消息typeOrderEventstruct{OrderIDstringjson:order_idAmountfloat64json:amountStatusstringjson:status}event,_:json.Marshal(OrderEvent{OrderID:O-1024,Amount:99.9,Status:paid})n,_:rdb.Publish(ctx,orders:new,event).Result()fmt.Printf(消息触达 %d 个订阅者\n,n)PUBLISH的返回值是收到消息的订阅者数量——如果返回 0说明没人监听消息丢了。这是 Pub/Sub 的fire-and-forget天性。模式订阅用通配符监听一批频道// 订阅所有 order 相关事件orders:created, orders:cancelled, orders:paid...pubsub:rdb.PSubscribe(ctx,orders:*)ch:pubsub.Channel()formsg:rangech{fmt.Printf([%s → %s] %s\n,msg.Pattern,msg.Channel,msg.Payload)}*匹配一个层级如order:*匹配order:new但不匹配order:item:stock。如果需要多级匹配可以用order:**取决于 Redis 版本。四、Pub/Sub 的消息可靠性问题Pub/Sub 设计上是一条广播水管——水流过就没了。什么时候会丢消息场景后果订阅者未上线时发布消息消息丢失订阅者的 Channel 缓冲区满处理慢消息积压Redis 客户端可能主动断连网络闪断期间的消息丢失Redis 宕机重启所有订阅关系 未发消息全部消失解决方案Pub/Sub Stream 双通道Pub/Sub 负责实时推送Stream 负责消息持久化Publisher ──PUBLISH── Redis Pub/Sub ──实时推送── Subscriber ──XADD──── Redis Stream ──补偿读取── Subscriber掉线重连后在线时吃 Pub/Sub 的实时推送低延迟掉线后从 Stream 里按消费者组的 LastDeliveredID 往回拉取遗漏的消息这不是 Redis 内建功能而是应用层组合拳。五、Pub/Sub vs 其他消息模式维度Pub/SubRedis StreamsRabbitMQKafka消息持久化❌ 不持久化✅✅✅消费确认❌ 无✅ XACK✅ ACK✅ offset commit回溯重复消费❌✅✅✅延迟 1ms 1ms~ ms 级~ ms 级运维复杂度极低低中高适用场景实时广播、缓存失效通知、WebSocket 跨节点同步事件日志、消息队列、消费者组企业消息中间件大数据流处理一句话选型要实时 丢几条没事 → Pub/Sub要可靠 不能丢 → Streams。六、常见应用场景1. 跨节点缓存失效多个 Web 节点各自有本地缓存数据更新后通过 Pub/Sub 广播一个cache:invalidate:user:42所有节点同时清掉本地缓存。这是 Redis Pub/Sub 最经典的生产场景。2. WebSocket 跨节点消息推送用户连在节点 A 的 WebSocket 上但触发消息的服务跑在节点 B 上。B 发 Pub/Sub 到 RedisA 订阅并推给 WebSocket 客户端。Socket.IO 的 Redis Adapter 就是这个原理。3. 配置热更新配置中心更新配置后PUBLISH 一条通知。所有服务订阅了config:reload收到后立刻拉取最新配置。七、本章要点要点一句话Pub/Sub 是广播不是队列消息发完就消失没有重放、没有回执一条长连接一个 PubSubSubscribe 后连接被专用不要混用PSubscribe 用*通配可以监听整批频道省去逐个 SUBSCRIBE可靠性靠组合拳Pub/Sub 做实时通道Stream 做补偿兜底延迟极低亚毫秒级适合实时场景核心认知把 Pub/Sub 当成一个广播喇叭而非消息仓库来用就对了。
返回列表