[redis系列] 发布订阅 Pub/Sub

介绍

Redis 的发布/订阅(Pub/Sub)模式允许发布者通过通道广播消息,发布者不关心是否有订阅者;订阅者根据兴趣接收相关消息,而无需了解具体的发布者。这种机制通过将发布者和订阅者解耦,使得它们不直接依赖于对方,大大提高了系统的扩展性。

如果您对 Redis 相关内容感兴趣,欢迎查看我的 Redis 系列博客。


匹配订阅

SUBSCRIBE

该命令返回值的第三个表示当前客户端已订阅的频道总数。

# 订阅频道my_channel, subscribe可以同时订阅多个频道
127.0.0.1:6379> SUBSCRIBE my_channel
1) "subscribe"
2) "my_channel"
3) (integer) 1

UNSUBSCRIBE

该命令返回值的第三个表示在取消订阅之后,当前客户端剩余订阅的频道总数。当第三个元素为零时,表示客户端已取消订阅所有频道,此时客户端可执行任何类型的 Redis 命令,因为它已退出 Pub/Sub 状态。

# 取消订阅频道my_channel, UNSUBSCRIBE 可以同时取消订阅多个频道
127.0.0.1:6379(subscribed mode)> UNSUBSCRIBE my_channel
1) "unsubscribe"
2) "my_channel"
3) (integer) 0

PUBLISH

该命令返回值是一个整数,表示成功发送到指定频道的订阅者数量。

# 发布消息new_message到频道my_channel
127.0.0.1:6379> PUBLISH my_channel new_message
(integer) 1


模式匹配订阅

Redis 的 发布/订阅 支持模式匹配,客户端可以使用此机制来订阅与特定模式匹配的多个频道,从而接收发送到这些频道的所有消息。

PSUBSCRIBE

# 按pattern订阅频道, 所有符合这个pattern的频道都会被订阅, 可以同时订阅多个pattern
127.0.0.1:6379> PSUBSCRIBE my_*
1) "psubscribe"
2) "my_*"
3) (integer) 1

PUNSUBSCRIBE

# 按pattern取消订阅频道, 所有符合这个pattern的频道都会被取消订阅, 可以同时取消订阅多个pattern
127.0.0.1:6379(subscribed mode)> PUNSUBSCRIBE my_*
1) "punsubscribe"
2) "my_*"
3) (integer) 0


重复消息

当一个客户端同时订阅了多个模式或同时订阅了模式和频道时,它可能会多次接收到相同的消息。这种情况可以通过以下示例来说明:

假设客户端订阅了:

127.0.0.1:6379> SUBSCRIBE my_channel
1) "subscribe"
2) "my_channel"
3) (integer) 1
127.0.0.1:6379(subscribed mode)> PSUBSCRIBE my_*
1) "psubscribe"
2) "my_*"
3) (integer) 2

在这种情况下,如果有一条消息被发布到 my_channel 频道,客户端将接收到两条消息。

# publish
127.0.0.1:6379> publish my_channel 111
(integer) 2

# subscribe
1) "message"
2) "my_channel"
3) "111"
1) "pmessage"
2) "my_*"
3) "my_channel"
4) "111"


模式匹配的订阅计数

在 SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE 和 PUNSUBSCRIBE 等消息类型中,响应的最后一个参数表示客户端当前仍然活跃的订阅数量。这个数字是客户端仍然订阅的频道和模式的总数。因此,只有当该计数值降为零,即客户端取消订阅了所有频道和模式后,客户端才会退出 Pub/Sub 状态。

下面给出通过 Python 操作 Redis 的 Pub/Sub 模式的例子,让客户端从进入 Pub/Sub 状态到退出 Pub/Sub 状态。

import redis

r = redis.Redis(host='localhost', port=6379, db=0)

pubsub = r.pubsub()
# 订阅两个频道
pubsub.subscribe('news')
pubsub.subscribe('paper')
# 开始监听
for message in pubsub.listen():
    if message['type'] == 'message':
        print(f"Received: {message['data'].decode()}")

    # 此时还剩一个频道, pubsub.listen()继续监听消息
    if has_accept_3_message():
        pubsub.unsubscribe('news')
    # 此时取消订阅所有频道, pubsub.listen()将退出监听
    if has_accept_10_message():
        pubsub.unsubscribe('paper')


消息传递语义

Redis 的 发布/订阅 模式保证每条消息最多只发送一次。如果有订阅者,它就会接收到这条消息;如果没有,消息就会丢失。一旦消息被发送出去,Redis 不会保存它,也不会重新发送。如果订阅者因为网络问题或其他原因没能及时收到消息,那么这条消息就永远丢失,不会再发送。

如果你的应用需要更强的消息传递保障,可以考虑使用 Redis Streams。与 Pub/Sub 模式不同,Redis Streams 中的消息是持久化存储的,且支持更丰富的消息传递语义,提供了更高的可靠性和消息投递保障。


分片发布/订阅(Sharded Pub/Sub)

从 Redis 7.0 起,Redis 引入了分片发布/订阅机制。在这种模式下,消息会根据其所属的“分片频道”被路由到特定的节点。类似于数据键的分配方式,分片频道也会根据特定的算法被分配到某个槽上,每条分片消息必须通过与该槽对应的节点进行传递,集群会确保消息正确地转发到所有负责该分片的节点。客户端可以通过连接负责该分片的主节点或其副本来订阅相应的分片频道,实现分片发布/订阅功能的命令包括 SSUBSCRIBESUNSUBSCRIBESPUBLISH

分片发布/订阅的主要优势在于,它有效地扩展了 Redis 集群中的发布/订阅功能。通过限制消息的传播范围,仅在相应的分片内传递消息,它避免了全局发布/订阅模式中每条消息都需要传播到整个集群的问题。这不仅减少了集群总线的数据传输量,还通过增加更多分片来实现发布/订阅功能的水平扩展。


参考

Redis Pub/Sub | Docs

你可能感兴趣的:(redis,数据库,缓存)