当前位置:首页 > 问答 > 正文

Redis里头怎么搞消息发布和订阅,聊聊它的实现原理和用法

Redis 的发布订阅功能,说白了就是一种简单的消息传递模式,它允许一个客户端(发布者)发送消息,而多个其他客户端(订阅者)可以接收这些消息,这和我们日常生活中听收音机或者关注微信公众号很像:电台(发布者)广播节目,所有调到这个频率的听众(订阅者)都能听到;公众号发文,所有关注它的粉丝都能看到。

怎么用?几个核心命令

用起来非常简单,主要就用几个命令。

  1. 订阅:SUBSCRIBE 想接收消息的客户端,就用 SUBSCRIBE 命令来“订报纸”,一个客户端执行 SUBSCRIBE news.sports,它就订阅了名为 “news.sports” 的频道,一旦执行这个命令,客户端就会进入等待消息的状态,阻塞在那里,直到有消息到来或者连接关闭,一个客户端可以同时订阅多个频道,用 SUBSCRIBE channel1 channel2 ...

    Redis里头怎么搞消息发布和订阅,聊聊它的实现原理和用法

  2. 发布:PUBLISH 要发送消息的客户端,就用 PUBLISH 命令来“发报纸”,另一个客户端执行 PUBLISH news.sports "中国队夺冠!",Redis 服务器就会把这条“中国队夺冠!”的消息,发送给所有当前订阅了 “news.sports” 频道的客户端。

  3. 按模式订阅:PSUBSCRIBE 有时候频道很多,“news.sports.football”、“news.sports.basketball”、“news.tech.ai”,如果一个客户端想订阅所有和体育相关的新闻,不用一个一个去订阅,它可以用通配符来批量订阅,命令是 PSUBSCRIBE,执行 PSUBSCRIBE news.sports.*,那么所有以 “news.sports.” 开头的频道发布消息,这个客户端都能收到。

  4. 退订:UNSUBSCRIBE 和 PUNSUBSCRIBE 顾名思义,就是取消订阅某个或某些频道。UNSUBSCRIBE 对应普通的 SUBSCRIBEPUNSUBSCRIBE 对应模式的 PSUBSCRIBE

实现原理是啥?

Redis里头怎么搞消息发布和订阅,聊聊它的实现原理和用法

Redis 自己是单线程的,但它的发布订阅功能实现起来很高效,原理可以想象成一个大本子或者一个广播站的黑板。

  1. 频道字典 在 Redis 服务器内部,维护了一个叫做 pubsub_channels 的字典(键值对集合),这个字典就是那个“大本子”。

    • :就是频道名,“news.sports”。
    • :是一个链表,这个链表里保存了所有订阅了这个频道的客户端的信息(可以理解为客户端连接的地址)。
  2. 订阅过程 当一个客户端执行 SUBSCRIBE news.sports 时,Redis 就会去翻这个“大本子”:

    • 看看字典里有没有 “news.sports” 这个键。
    • 如果没有,就在字典里新建一个 “news.sports” 键,对应的值是一个空链表。
    • 把这个客户端的连接信息,添加到 “news.sports” 对应的链表末尾。
  3. 发布过程 当一个客户端执行 PUBLISH news.sports "消息" 时,Redis 的行动是:

    Redis里头怎么搞消息发布和订阅,聊聊它的实现原理和用法

    • 继续翻“大本子”,找到 “news.sports” 这个键。
    • 拿到这个键对应的客户端链表。
    • Redis 会顺着这个链表,一个一个地、依次把“消息”发送给链表里的每一个客户端。
  4. 模式订阅的另类处理 对于 PSUBSCRIBE 这种模式订阅,Redis 有另外一个独立的字典叫 pubsub_patterns 来管理,它的值也是客户端链表,但键是模式本身(“news.sports.”),当发布消息时,Redis 除了在 pubsub_channels 里找完全匹配的频道,还会遍历 pubsub_patterns 字典里的所有模式,看看消息的频道名是否匹配某个模式(“news.sports.football” 匹配 “news.sports.”),如果匹配,就把消息也发给订阅了那个模式的客户端。

重要特性和注意事项

了解了基本用法和原理后,有几个关键点必须知道,这决定了它适合用在什么场景。

  • 没有消息堆积能力:这是最重要的一点,Redis 的发布订阅是“即发即弃”的,如果发布消息时,没有在线订阅者,那么这条消息就永远消失了,Redis 不会为它留任何副本,同样,如果一个订阅者中途断线了,重连之后,它收不到断线期间错过的任何消息,这和 RabbitMQ、Kafka 等专业的消息队列有本质区别。
  • 消息是直接推送的:只要订阅者在,消息一来就会立刻被推送到客户端,是实时的。
  • 稳定性:因为 Redis 是内存数据库,所以发布订阅的速度非常快,适合对实时性要求高,但允许少量消息丢失的场景,比如在线聊天室、实时通知、服务器间简单的信号传递等。
  • 不适合重要业务消息:如果你的业务场景要求消息绝对不能丢(比如订单处理、支付结果),那么不应该首选 Redis 的发布订阅,而应该用更专业的、支持消息持久化和确认机制的消息中间件。

总结一下,Redis 的发布订阅是一个轻量级、快速、但功能也相对简单的消息通知机制,它的核心就是通过服务器内部维护的订阅者列表,将发布者的消息进行一次性的广播,用它之前,一定要清楚它“不保证消息必达”的特点。

(主要思想参考自《Redis设计与实现》一书以及Redis官方文档中关于Pub/Sub的章节)