现在几乎所有的系统都会用到Redis,但并不是所有的系统都会用到消息队列。如果想使用消息队列,但又不想单独安装一个,可以使用Redis的消息队列功能。
Redis Messaging (Pub/Sub)
Redis消息队列一共只有六个命令,可以分为三类:
一、和订阅相关的命令
1、订阅一个或多个具体的channel:
SUBSCRIBE channel [channel ...]

四个订阅者,两个订阅like-man,两个订阅like-woman。
2、订阅一个或多个含有通配符的pattern:
PSUBSCRIBE pattern [pattern ...]

两个订阅者,都订阅like-*。
其中,通配符支持以下情况:
h?llo可以对应hello、hallo和hxllo
h*llo可以对应hllo和heeeello
h[ae]llo可以对应hello和hallo,但不能对应hillo
3、取消订阅一个或多个具体的channel:
UNSUBSCRIBE [channel [channel ...]]
备注:如果不指定channel,则取消订阅所有channel。
4、取消订阅一个或多个含有通配符的pattern:
PUNSUBSCRIBE [pattern [pattern ...]]
备注:如果不指定pattern,则取消订阅所有pattern。
二、和发布相关的命令
发布一个message到具体的channel:
PUBLISH channel message

向like-man发送一个消息handsome。

有四个订阅者接收到该消息,分别是两个订阅like-man的和两个订阅like-*的。

向like-woman发送一个消息sexy。

有四个订阅者接收到该消息,分别是两个订阅like-woman的和两个订阅like-*的。
三、和自省相关的命令
列出活跃的channel:
PUBSUB CHANNELS [pattern]
可以指定一个pattern或不指定,不指定时列出所有。

备注:活跃的channel是指至少有一个订阅者的。
列出channel的订阅者个数:
PUBSUB NUMSUB [channel-1 ... channel-N]
至少要指定一个channel,可以指定多个。

两个channel分别都有两个订阅者。
使用SpringBoot发布Redis消息
发布消息就是发送消息,这个比较简单,使用RedisTemplate即可。

使用SpringBoot订阅Redis消息
订阅消息就是接收消息,这个比较复杂。既有对Redis连接的管理,也有对消费消息的线程池的管理。不过Spring已经把这个“重活”给干了。
Spring提供了一个全套的解决方案,这里面包括:
1、订阅/取消订阅这些相关的用户操作
2、接收所有来自Redis的消息
3、把这些消息按照订阅关系分发给具体的消费者
4、触发消费消息的回调代码在线程池中运行
由于Spring已经全权代理,用户只需要提供要消费的topic以及对应的消费回调代码即可。
我们需要了解Spring提供的几个接口和类,才可以很好的使用:
Topic接口,表示一个订阅对象:

它有两个实现类,ChannelTopic和PatternTopic,前者对应redis的channel,后者对应redis的pattern。


MessageListener接口,回调接口,通过它来执行业务代码:

Message接口,表示从redis接收到的消息:

RedisMessageListenerContainer类,这个核心类,相当于一个代理,就是它负责接收redis的消息,并分发给MessageListener。
这个类需要一个RedisConnectionFactory,即redis连接工厂,用来获取一个redis连接,由于这个连接用于接收消息,所以它是一直阻塞着的。
还可以为这个类指定一个Executor,即线程池,这不是必须的,如果不指定它会生成一个默认的。
具体的配置代码
明白了原理后,就会变得很简单。我们只需成对的提供Topic和MessageListener即可,Topic告诉Spring要接收什么消息,MessageListener告诉Spring如何消费这些消息。
所以,我们自己定义一个接口,用于把它俩封装起来,接口名为MessageConsumer,即消息消费者。

我们只需实现该接口,并把实现类注册为Bean,就成为了一个消费者了。
LikeMan消费者

LikeWoman消费者

LikeBoth消费者

最后需要注册核心类(RedisMessageListenerContainer)为Bean,然后再收集到所有注册的消费者(MessageConsumer)Bean,并把它们添加到核心类里面即可。

看看执行情况
当没有启动Spring的时候,redis里没有订阅。

当启动好Spring的时候,redis里已经有了我们的两个订阅。

可以看到like-man和like-woman都各自有一个订阅者。

可以看到订阅的pattern只有一个。

我们向like-man发送一个消息,提示有两个订阅者收到了该消息。

可以看到订阅like-man的和订阅like-*的都收到了该消息。

同样向like-woman发送一个消息,也有两个订阅者收到了消息

同样订阅like-woman的和订阅like-*的都收到了该消息。

搞点其它尝试
我们再注册一份消费者,这样每个topic或pattern都有两个消费者。

发现redis里对于like-man和like-woman的订阅数并没有变。

那再来向like-man发个消息看看,发现还是两个订阅者收到了该消息。

再看看具体消费消息的情况,发现确实是四个消费者。

简单对比之后,我们发现了订阅者和消费者的关系是一对多的。
订阅描述的是接收消息的事情,消费描述的是使用消息的事情。
如果在Spring里有多个消费者订阅同一个topic,那么Spring和redis之间关于该topic只有一份订阅关系,所以消息只接收一次,只不过Spring再分别分发给多个消费者而已。这就是前面说的代理作用嘛。
那我们就启动两份Spring工程,这样就有两个和redis之间的连接,此时理论上订阅数量应该变成2了。

果然和我们猜想的一样。
站在Spring的角度来看:
1、Spring保证在它和redis之间的一条连接上,对于同一个topic,只会向redis订阅一次,但是该topic可以有多个消费者。
2、Spring从这条连接上接收到这个topic的消息后,分发给这个topic的所有消费者。
站在Redis的角度来看:
1、Redis保证它和客户端的一条连接上只会建立一个订阅通道,而且在客户端首次订阅时建立。
2、这个订阅通道和topic无关,它是被所有这个客户端的topic共用。
可以用一个粗略的比喻来说明一下:
1、订阅通道就是一条路。
2、topic就是一辆车。
3、消费者就是一个人。
它们之间的关系是:
多个人可以坐到一辆车上,多辆车可以走在一条路上。
各自的关注点是:
1、Redis主要关注路,只要路建立好了,多少车都可以走。
2、Spring主要关注车,只要车有了,多少人都可以坐。
3、用户代码主要关注人,因为人有了,多少业务都可以处理。
由此可见,对于同一件事情,在不同的层次看到的“景色”是不一样的。
总的来说:
Redis利用它维护的订阅关系,把一个topic的消息发送给所有订阅该topic的客户端。
客户端利用它维护的消费关系,把一个topic的消息发送个所有消费该topic的消费者。
消费者拿到一个topic消息,根据topic和消息内容来进行适合的业务处理。
一个人能够看到什么样的风景,取决于他所在的高度。
一个人想要看到什么风景,必须先达到与之对应的高度。
(END)
>>> 热门文章集锦 <<<
11年码农的肺腑之言,如何成为一个优秀的程序员,送给渴望优秀的人
非著名架构师告诉你,代码该如何写,才能自己写的容易别人看的也不痛苦
爸爸又给Spring MVC生了个弟弟叫Spring WebFlux
【面试】吃透了这些Redis知识点,面试官一定觉得你很NB(干货 | 建议珍藏)
【面试】如果你这样回答“什么是线程安全”,面试官都会对你刮目相看(建议珍藏)
【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)
【面试】一篇文章帮你彻底搞清楚“I/O多路复用”和“异步I/O”的前世今生(深度好文,建议珍藏)
文章转载自编程新说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




