「进击Redis」十六、Redis 发布/订阅原理解析

前言

Redis 系列的第十六篇,Redis 的 pub/sub(发布订阅模式),虽然这个在之前的 Redis 系列第一篇初识 Redis中有提到正常不会使用这个来做消息队列系统。但是,Redis 本身是支持这个功能的,那么还是有必要学习一波,一方面假设好哥哥们公司没有用专门的消息队列中间件,那么这个时候你就可以提出来用 Redis 来实现了(不会 Redis 都没有用吧)。另一方面,这个可以在后续的面试中可以酌情跟面试官聊一聊,有的面试官也有主动问这个的(猛男我就遇到过,那个时候还是个萌新,虽然现在也是,啥都不懂,直接给我整蒙了,我不就面试个初级吗)。现在你在问问我试试,哈哈哈哈…
萌新

概述

在将发布/订阅模式前,好哥哥们是否也被问道过发布/订阅和观察者模式的区别。一开始是不是都觉得这两个玩意都差不多,实际上还是有区别的

观察者模式

概念:一个目标物件(被观察者)管理所有相依于它的观察者物件,并且在它本身的状态则会自动通知依赖它的对象。观察者模式属于行为型模式。
做饭

栗子:
90 后的童年总是会三五成群的一起约着去田里做泥鳅(农村来的孩子都没有玩具,只能玩泥巴,但是我们快乐呀)。但是去之前老妈都会叫注意安全,差不多就回家吃饭。于是乎,快乐的时间总是短暂的。中午很快就到了,这个时候就会关注一个事情,“午饭好了么,是否该回家吃饭了”。那是有没有手机(可能有了,但是也买不起呀),那咋办呢。机智如你。我们总是要每隔一段时间就跑回家问一下饭是不是做好了,没有就在出去玩,好了就洗手吃饭(像极了用JS轮询接口拉取数据)。
慢慢的,手机成为了一个必备品。我们也不用跑回去问老妈饭是不是做好了,到饭点了,你妈妈总是会主动打电话告诉你饭做好了,要回家吃饭了(以事件为驱动,触发推送通知)。
好哥哥们,这是不是和符合观察者模式。被观察者(老妈)、观察者(玩泥巴的我们)、事件(老妈是否做好饭)和由被观察者维护的观察者列表(我和小老弟)。

发布订阅模式

概念:发布/订阅是一种消息通信模式,消息的发送者(发布者)不会将消息直接发送给特定的接收者(订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者可能存在、感兴趣哪个类别。同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息。
Redis 提供了基于发布/订阅模式的消息机制,此种模式下,消息发布者和订阅者不进行直接通信,发布者客户端向指定的channel(频道)发布消息,订阅该频道的每个客户端都可以收到该消息,如下图
订阅

栗子:
我丙哥(订阅者)经常在小破站上的舞蹈专区(类别)学习跳舞,丙哥可以订阅任意数量感兴趣的频道(跳舞、二次元),同时也不 care 是谁发布的这个频道(只要能学跳舞就行),只接收自己感兴趣频道的消息。另一方面,小破站(发布者)也不需要知道谁关注了哪些频道,只是会更新对应频道的内容(消息)

对比

  1. 观察者并非一成不变。并不是每天都是所有人回家吃午饭,只有提前告知老妈要回家吃午饭的人才会接到电话。因为观察者的可变性,需要被观察者维护一个列表。
  2. 观察者模式可以便捷的完成目标。不需要观察者不停的轮询查看事件变化,也不需要被观察者多次询问观察者意愿。只需要观察者提前加入或离开列表,便可以由被观察者准确的进行事件通知。 说起来,微信订阅号便是观察者模式的一种实现。
  3. 发布/订阅不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。

命令

Redis 主要提供了发布消息、订阅频道、取消订阅以及按照模式订阅和取消订阅等命令。

1 发布消息

往舞蹈频道推送消息

## 格式 channel:频道, message : 消息
publish channel message
## 发布消息到舞蹈频道,返回结果为订阅者个数,没有则返回0
127.0.0.1:6379> publish channel:dance "put your hands!"
(integer) 0

2 订阅消息

订阅者可以订阅一个或多个频道,下面操作为当前客户端订阅了channel

## 格式 channel:频道
subscribe channel [channel ...]
127.0.0.1:6379> subscribe channel:dance
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:dance"
3) (integer) 1
## 新起一个客户端往channel中发送消息
127.0.0.1:6379> publish channel:dance "are your ok"
(integer) 1
## 切换到订阅者客户端会收到如下消息
127.0.0.1:6379> subscribe channel:dance
Reading messages... (press Ctrl-C to quit)
...
1) "message"
2) "channel:dance"
3) "are your ok"

需要注意的是新开启的订阅客户端,无法收到该频道之前的消息,因为 Redis 不会对发布的消息进行持久化,和很多专业的消息队列系统(例如KafkaRocketMQ)相比,Redis 的发布订阅略显粗糙,例如无法实现消息堆积和回溯和很多专业的消息队列系统(例如 Kafka、RocketMQ)相比,Redis 的发布订阅略显粗糙,例如无法实现消息堆积和回溯客户端在执行订阅命令之后进入了订阅状态,只能接收subscribepsubscribeunsubscribepunsubscribe的四个命令。

3 取消订阅

客户端可以通过unsubscribe命令取消对指定频道的订阅,取消成功后,不会再收到该频道的发布消息

## 格式 channel:频道
unsubscribe [channel [channel ...]]
## 发布消息到舞蹈频道,返回结果为订阅者个数,没有则返回0
127.0.0.1:6379> unsubscribe channel:dance
1) "unsubscribe"
2) "channel:dance"
3) (integer) 0

4 按照模式订阅和取消订阅

除了subcribeunsubscribe命令,Redis 命令还支持 glob 风格(shell 下简化的正则表达式)的订阅命令psubscribe和取消订阅命令punsubscribe

## 格式 pattern: 表达式
psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]
## 订阅以Java开头的频道
127.0.0.1:6379> psubscribe Java*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "Java*"
## 取消以Java开头的订阅
127.0.0.1:6379> punsubscribe Java*
Reading messages... (press Ctrl-C to quit)
1) "punsubscribe"
2) "Java*"
3) (integer) 0

5 查询订阅

查看活跃的频道

活跃的频道是指当前频道至少有一个订阅者,其中[pattern]是可以指定具体的模式

## 格式
pubsub channels [pattern]
## 查询活跃的订阅
127.0.0.1:6379> pubsub channels channel:*d*
1) "channel:dance"
2) "channel:durid"
查看频道订阅数
## 格式
pubsub numsub [channel ...]
## 订阅数
127.0.0.1:6379> pubsub numsub channel:dance
1) "channel:dance"
2) (integer) 1
查看模式订阅数

通过模式订阅的数量

## 格式
pubsub numpat
## 数量
127.0.0.1:6379> pubsub numpat
(integer) 1

原理解析

订阅

在 Redis 的底层结构中,Redis 服务器结构体中定义了一个 pubsub_channels 字典, 维护了频道和订阅者的对应关系,每次客户端添加订阅时将对应的客户端信息添加到链表的末尾就好了。
订阅

发布

发布消息时现根据chanel找到对应订阅者链表,然后遍历发送消息就 OK 了。

使用场景

  1. IM 实时通信聊天室
  2. 公告/告示牌、系统消息通知
  3. 微信公众号等类似的场景
  4. 系统业务逻辑功能解耦

总结

上面有提到发布订阅和观察者模式的区别,好哥哥们可以理解一下,面试场景还是经常会遇到的。另外,Redis 的发布订阅功能没有像RocketMQkafka 这种消息队列中间那么强大,但是这种方式简单。好哥哥们应该不会用这个来做消息队列吧。

本期就到这啦,有不对的地方欢迎好哥哥们评论区留言,另外求关注、求点赞

下一篇: 保证你没用过Redis GEO
上一篇: 奇妙的 Redis HyperLogLog

猜你喜欢

转载自blog.csdn.net/qq_34090008/article/details/111410731
今日推荐