Redis发布订阅实践场景和实现

2,850 阅读4分钟

🌈简介

Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分:发布者订阅者Channel。发布者和订阅者都是Redis客户端,Channel则为Redis服务器端,发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。Redis的这种发布订阅机制与基于主题的发布订阅类似,Channel相当于主题。

下图展示了频道 channel,以及订阅这个频道的三个客户端:client1 、 client2 和 client3 之间的关系:

Redis-发布订阅-subscribe.png

当有新消息通过PUBLISH命令发送给频道channel时,这个消息就会被发送给订阅它的三个客户端:

Redis-发布订阅-message.png

🎈命令

发布消息PUBLISH

时间复杂度:O(N+M),其中 N 是频道 channel 的订阅者数量,而 M 则是使用模式订阅(subscribed patterns)的客户端的数量。

✏️语法:PUBLISH channel message
📃说明:将信息message发送到指定的频道channel。
🆗返回值:接收到信息message的订阅者数量。

image.png

上图中,我们使用 publish 指令向 pay_result_channel 这个频道发送了一条消息。我们可以看到 redis 向我们返回0,这其实代表当前订阅者个数,由于此时没有订阅,所以返回结果为0。

订阅频道消息: SUBSCRIBE

✏️语法:SUBSCRIBE channel [channel ...]
📃说明:订阅一个或者多个频道
🆗返回值:返回订阅的频道信息和消息内容。

image.png

如上图所示,我们订阅 pay_result_channel 这个频道,当有其他客户端往这个频道发送消息,当前订阅者就会收到消息。

模拟发布一条消息message is comming到频道channel:

image.png

当前订阅者就会接收到一条消息: image.png

另外,我们在使用订阅命令的时候,需要注意几点:

1️⃣ 客户端执行订阅指令之后,就会进入订阅状态,之后就只能接收 subscribepsubscribeunsubscribepunsubscribe 这四个命令。

2️⃣ 新订阅的客户端,是无法收到这个频道之前的消息,这是因为 Redis 并不会对发布的消息持久化的。

除了上面的功能以外的,Redis 还支持模式匹配的订阅方式。简单来说,客户端可以订阅一个带 * 号的模式,如果某些频道的名字与这个模式匹配,那么当其他客户端发送给消息给这些频道时,订阅这个模式的客户端也将会到收到消息

使用 Redis 订阅模式,我们需要使用一个新的指令 psubscribe:

psubscribe pay.*

那么一旦有其他客户端往 pay 开头的频道,比如 pay_result_channelpay_xxx,我们都可以收到消息。

如果需要取消订阅模式,我们需要使用相应punsubscribe 指令,比如取消上面订阅的模式:

punsubscribe pay.*

🔥Redis 客户端发布订阅使用方式

基于 Jedis 开发发布/订阅

聊完 Redis 发布订阅指令,我们来看下 Java Redis 客户端如何使用发布订阅。

Maven版本:

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.2.0</version>
</dependency>

使用jedis实现发布订阅很简单,只需要调用Jedis类的publish方法即可实现:

// 生产环境不建议这么使用哦,推荐使用 JedisPool 线程池的方式 
Jedis jedis = new Jedis("localhost", 6379);
jedis.auth("xxxxx");
jedis.publish("pay_result_channel", "pay success!");

订阅的代码也不复杂,我们只需要继承 JedisPubSub实现里面的相关方法,一旦有其他客户端往订阅的频道上发送消息,将会调用 JedisPubSub相应的方法。

/**
 * 自定义消息监听对象
 *
 * @author: jacklin
 * @date: 2022-05-04 22:43
 */
@Component
public class MyCustomListener extends JedisPubSub {

    @Override
    public void onMessage(String channel, String message) {
        System.out.println("收到订阅频道:" + channel + "的消息:" + message);
        super.onMessage(channel, message);
    }

    @Override
    public void onPMessage(String pattern, String channel, String message) {
        System.out.println("收到具体订阅频道:" + channel + "订阅模式:" + pattern + " 的消息:" + message);
        super.onPMessage(pattern, channel, message);
    }
}

当有其他客户端往 pay_result_channel频道发送消息时,订阅将会收到消息。

Connected to the target VM, address:'127.0.0.1:6379',transport:'socket'
收到订阅频道:pay_result_channel的消息:pay_suceess!
收到订阅频道:pay_result_channel的消息:pay_suceess_again!

以上仅仅是简单的实现逻辑,正式项目可以根据自己的业务场景加以实现~

总结

今天我们主要介绍 Redis 发布订阅功能,主要对应的 Redis 命令为:

  • subscribe channel [channel ...] 订阅一个或多个频道
  • unsubscribe channel 退订指定频道
  • publish channel message 发送消息
  • psubscribe pattern 订阅指定模式
  • punsubscribe pattern 退订指定模式

我们可以利用 Redis 发布订阅功能,实现的简单 MQ 功能,实现上下游的解耦。 不过需要注意了,由于 Redis 发布的消息不会被持久化,这就会导致新订阅的客户端将不会收到历史消息。

所以,如果当前的业务场景不能容忍这些缺点,那还是用专业消息队列MQ来实现。

收藏⭐️+关注👨‍🎓+留言📃