Redis实现消息队列之发布订阅模式

原创
2020/06/18 07:30
阅读数 395

发布订阅(pub/sub)是一种消息通信模式:发送者(pub)在某一频道发送消息,订阅者(sub)接收消息。发布订阅模式类似与微博关注,比如说博主mango被张三、李四、王五关注,那么mango发一篇微博的时候张李王三人都会从关注里看到这条微博。

那么发布订阅和生产消费有何异同之处呢?生产消费主要是生成一个消息只能被一个客户端消费,而发布订阅可以理解为发布一条消息,在该频道中的所有客户端都会收到,所以有时候我们这个发布订阅类似广播。

注意pubsub是一个数据结构。

Pub/Sub(发布订阅)

前面提到客户端能接受到消息首先要订阅频道,那么redis中的订阅命令subscribe channel [channel ...] 这里可以订阅多个频道。订阅后我们需要发布消息到这个频道中publish channel message。

####订阅test频道> subscribe testReading messages... (press Ctrl-C to quit)1) "subscribe"2) "test"3) (integer) 1
####发布信息> publish test 'hello'(integer) 1

不是可以广播嘛,那么我们再起一个客户端

当然redis还提供退订unsubscribesubscribe 的使用方式一样。

PUBSUB命令

PUBSUB是一个查看订阅与发布系统状态的内省命令,它由数个不同格式的子命令组成。

PUBSUB CHANNELS [pattern] 列出当前的活跃频道。活跃频道指的是那些至少有一个订阅者的频道, 订阅模式的客户端不计算在内。

pattern 参数是可选的:

  • 如果不给出 pattern 参数,那么列出订阅与发布系统中的所有活跃频道。

  • 如果给出 pattern 参数,那么只列出和给定模式 pattern 相匹配的那些活跃频道。

> pubsub channels1) "test"

PUBSUB NUMSUB [channel-1 ... channel-N] 列出当前的活跃频道和返回连接数

> pubsub numsub test1) "test"        #频道2) (integer) 2   #连接数> pubsub numsub test11) "test1"2) (integer) 0

PUBSUB NUMPAT 返回订阅模式的数量。

注意,这个命令返回的不是订阅模式的客户端的数量,而是客户端订阅的所有模式的数量总和,可以理解为模式匹配,例如订阅一个test*,客户端能接收test、test1、test2等等这样的,看下面例子。

####客户端> psubscribe test*Reading messages... (press Ctrl-C to quit)1) "psubscribe"2) "test*"3) (integer) 1
> pubsub numpat(integer) 1

模式匹配订阅

介绍完发布订阅的一般模式,此时我们小伙伴就问,长得跟mango一样的我能不能订阅呢?当然redis是支持这种模糊订阅的,其命令为psubscribe,跟subscribe使用方式一致。

> psubscribe test*Reading messages... (press Ctrl-C to quit)1) "psubscribe"2) "test*"3) (integer) 1

PubSub原理

我们直到redis是key-value键值对的字典,PubSub前面讲过是一个数据结构,那么它是如何存储在内存中的呢?看老夫画图来解答。

我们从图中可以看到每个频道放入字典数组中,对应频道的订阅者则放入链表中,当我们发送一个publish命令时,首先字典数组遍历找到对应的频道,然后找到对应的订阅链,依次发送消息。

所以我们可以看出每次发送消息时我们的都需要遍历这个字典,也就是说它的执行时间效率为O(n),但是我们redis的宗旨是快,减少执行O(n)的命令,这违背了我们当初的初衷

PubSub的缺点

PubSub的发布者传递过来一个消息,Redis会直接找到相应的订阅者传递过去。如果一个订阅者都没有,那么消息会被直接丢弃。如果开始有三个订阅者,第三个订阅者突然挂掉了,发布者会继续发送消息,另外两个订阅者可以持续收到消息,但是当挂掉的订阅者重新连上的时候,在断连期间发布者发送的消息,对于这个发布者来说就是彻底丢失了。
如果Redis停机重启,PubSub的消息是不会持久化的,毕竟Redis开机就相当于一个订阅者都没有,所有的消息会被直接丢弃。正是因为PubSub有这些缺点,在消息队列的领域它几乎找不到合适的应用场景。
所以Redis的作者单独开启了一个项目Disque专门用来做多播消息队列,不过该项目目前没有成熟,直处于Beta版本。

填坑——为什么Redis不适合做消息队列

1.难以保证消息队列的ACK,消息发送出去后没有一个回馈过程,消息无法做持久化

2.如果保证消息持久化,那么必定损失性能,首先我们需要把消息存入磁盘,然后从磁盘中读取数据到内存去操作,这个过程是非常耗时的

3.PubSub执行效率低,执行效率是O(n),违背了redis设计初衷

4.难以实现复杂的消息模式

如果需要用到消息队列,还得需要使用专业的消息队列,毕竟这个技术已经相当成熟了


一名正在抢救的coder

笔名:mangolove

CSDN地址:https://blog.csdn.net/mango_love

GitHub地址:https://github.com/mangoloveYu


本文分享自微信公众号 - 架构技术栈(gh_f036ff0c58eb)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部