Skip to content

Redis发布订阅

redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息

Redis客户端可以订阅任意数量的频道

零基础可以先这样理解:

Redis Pub/Sub 是一个轻量级广播通道。发布者把消息发到频道,所有正在订阅这个频道的客户端都会立刻收到。

它适合在线通知、实时广播、简单聊天室等场景,但它不是可靠消息队列:订阅者离线时不会补收历史消息,也没有消费确认和重试。

为什么需要发布订阅

发布订阅解决的是“一条消息同时通知多个在线订阅者”的问题。

如果不用发布订阅,发布者需要知道每个接收者是谁,然后逐个调用:

mermaid
flowchart TD
    A["发布者"] --> B["调用客户端 A"]
    A --> C["调用客户端 B"]
    A --> D["调用客户端 C"]

这样发布者和接收者强耦合,新增订阅者就要改发布逻辑。

使用 Pub/Sub 后:

mermaid
flowchart TD
    A["发布者"] --> B["Redis Channel"]
    B --> C["订阅者 A"]
    B --> D["订阅者 B"]
    B --> E["订阅者 C"]

发布者只关心频道,不关心谁订阅。订阅者在线时就能收到消息。

风险边界

Redis Pub/Sub 的风险在于它只做实时广播,不做可靠投递:

风险后果
订阅者离线离线期间的消息不会补发
没有 ACK发布者不知道订阅者是否处理成功
没有重试处理失败不会自动重新投递
不适合堆积不能像 MQ 一样承接大量未消费消息

所以它适合实时通知,不适合订单创建、支付成功、库存扣减这类必须可靠处理的业务事件。

工作原理图

mermaid
flowchart TD
    A["Client A\nSUBSCRIBE news"] --> B["Redis Server"]
    C["Client B\nSUBSCRIBE news"] --> B
    D["Publisher\nPUBLISH news hello"] --> B
    B --> E{"查找频道 news\n对应的订阅客户端链表"}
    E --> F["把 hello 推送给 Client A"]
    E --> G["把 hello 推送给 Client B"]
    H["Client C 未订阅或离线"] -. "不会收到历史消息" .-> B

Redis 服务端内部会维护一个字典:key 是频道名,value 是订阅这个频道的客户端列表。发布消息时,Redis 找到频道对应的客户端列表,然后逐个推送。

订阅/发布消息图 pub-sub.png 下图展示了频道channel1,以及订阅这是三个频道的三个客户端--client2、client5、和client1之间的关系 sub.png

当有新消息通过publish命令发送给频道channel1时,这个消息就会被发送给订阅它的三个客户端 pub.png

命令

这些命令被广泛用于构建即时通信应用,比如聊天室(chatroom)和实时广播、实时提醒等 pub-1.png

测试

订阅端:

bash
127.0.0.1:6379> SUBSCRIBE lpx				#订阅一个频道	lpx
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "lpx"
3) (integer) 1
#等待读取信息
1) "message"	#消息
2) "lpx"		#频道
3) "hello,lpx"	#内容
1) "message"
2) "lpx"
3) "hello,redis"

发送端:

bash
127.0.0.1:6379> PUBLISH lpx "hello,lpx"		#发布者发布消息到频道
(integer) 1
127.0.0.1:6379> PUBLISH lpx "hello,redis"	#发布者发布消息到频道
(integer) 1

原理

Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,了解发布和订阅的底层实现,其次加深对redis的理解。

Redsi通过PUBLISH、SUBSCRIBE和PSUBSCRIBE等命令实现发布和订阅功能。

通过SUBSCRIBE命令订阅某频道后,redis-server里维护了一个字典,字典的键就是一个个channel(频道),而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSVRIBE命令的关键,就是将客户端添加到给定channel的订阅链表中。

通过PUBLISH命令向订阅者发布消息,redis-server会使用给定的频道作为键,在它维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。

PUB/SUB从字面上理解就是发布(publish)与订阅(subscrible),在Redis中,你可以设定对某一个key值进行消息发布及订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。

使用场景

1、实时消息系统

2、实时聊天(频道作为聊天室,将信息回显给所有人即可)

3、订阅、关注系统

稍微复杂的场景就会使用消息中间件来实现例如:MQ

Pub/Sub 和 MQ 的区别

对比点Redis Pub/SubRocketMQ / RabbitMQ 等 MQ
离线消息不保存,离线收不到通常可持久化,消费者上线后继续消费
消费确认没有 ACK通常有 ACK、重试、死信
消息堆积不适合大量堆积适合削峰、异步解耦
典型用途在线通知、广播订单事件、异步任务、可靠投递