Redis从入门到精通【进阶篇】之消息传递发布订阅模式详解

文章目录

  • 0. 前言
  • 1. 基本原理
    • 1.1 基于频道(Channel)的发布/订阅
    • 1.2 基于模式(Pattern)的发布/订阅
  • 2. Redis 发布订阅实际应用
    • 2.1 Redis Sentinel
    • 2.1 SpringBoot Redis发布/订阅
  • 3. Redis从入门到精通系列文章

在这里插入图片描述

0. 前言

发布订阅模式(Publish-Subscribe Pattern)是一种消息传递模式,其基本原理是消息的发送者(发布者)不会直接发送消息给特定的接收者(订阅者),而是将消息分成不同的类别(频道),然后将消息发送给订阅了这些类别的所有接收者。发布订阅模式在分布式系统中广泛应用,例如实时消息推送、日志收集等。

在 Redis 中,发布订阅模式有两个主要的角色:发布者和订阅者。发布者通过 PUBLISH 命令向指定的频道发送消息,而订阅者则通过 SUBSCRIBE 命令订阅/取消订阅指定的频道,并通过监听器(Callback)接收到发布者发送的消息。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
在这里插入图片描述

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

在这里插入图片描述

1. 基本原理

Redis 发布订阅是一种消息通信模式,通过这种模式可以让多个客户端之间进行消息的发布和订阅。Redis 提供了以下几个命令来实现发布订阅的功能:

  1. PUBLISH channel message:将消息 message 发送到指定的频道 channel 中,返回值为接收到消息的订阅者数量。
  2. SUBSCRIBE channel [channel …]:订阅一个或多个频道 channel,每当有新消息发布到订阅的频道时,就会收到相应的消息。
  3. UNSUBSCRIBE [channel [channel …]]:取消订阅一个或多个频道 channel,如果不指定 channel,则取消订阅所有频道。
  4. PSUBSCRIBE pattern [pattern …]:订阅一个或多个符合指定模式 pattern 的频道,每当有新消息发布到符合模式的频道时,就会收到相应的消息。
  5. PUNSUBSCRIBE [pattern [pattern …]]:取消订阅一个或多个符合指定模式 pattern 的频道,如果不指定 pattern,则取消订阅所有模式。
  6. PUBSUB subcommand [argument [argument …]]:查看订阅与发布系统状态,可以用来获取订阅与发布系统的各种信息,比如订阅者数量、频道列表等等。 其中,PUBLISH
    命令用于向指定的频道发布消息,SUBSCRIBE 命令用于订阅一个或多个频道,PSUBSCRIBE
    命令用于订阅一个或多个符合指定模式的频道,PUBSUB 命令用于查看订阅与发布系统状态。

1.1 基于频道(Channel)的发布/订阅

Redis 中的频道(Channel)相当于消息的分类,一个频道可以有多个订阅者,而一个订阅者也可以订阅多个频道。在 Redis 中,通过 PUBLISH 命令向指定的频道发送消息,而通过 SUBSCRIBE 命令来订阅/取消订阅指定的频道,并通过监听器接收到发布者发送的消息。

以下是 Redis 命令行界面中基于频道的发布/订阅示例:

# 订阅频道
127.0.0.1:6379> SUBSCRIBE news
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1# 发布消息
127.0.0.1:6379> PUBLISH news "Hello, world!"
(integer) 1# 订阅者接收到消息
1) "message"
2) "news"
3) "Hello, world!"

在 Redis 中,发布/订阅模式的实现基于 Redis 的事件机制,即订阅者通过执行 SUBSCRIBE 命令将自己的监听器添加到 Redis 服务器的事件循环器中,当发布者通过 PUBLISH 命令向指定频道发送消息时,Redis 服务器会将消息发送给监听该频道的所有订阅者。

具体来说,Redis 服务器会维护一个事件循环器,并在其中注册所有客户端的监听器。当客户端通过 SUBSCRIBE 命令订阅某个频道时,Redis 服务器会将该客户端的监听器添加到与该频道相关的事件处理器中,并在事件循环器中注册该事件处理器。当发布者通过 PUBLISH 命令向指定频道发送消息时,Redis 服务器会将消息发送给与该频道相关的事件处理器中的所有监听器,从而实现消息的发布和订阅。

1.2 基于模式(Pattern)的发布/订阅

Redis 还支持基于模式(Pattern)的发布/订阅,模式是一种特殊的频道,它可以匹配一个或多个频道。在 Redis 中,通过 PSUBSCRIBE 命令订阅/取消订阅匹配指定模式的频道,并通过监听器接收到发布者发送的消息。

以下是 Redis 命令行界面中基于模式的发布/订阅示例:

# 订阅模式
127.0.0.1:6379> PSUBSCRIBE news.*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news.*"
3) (integer) 1# 发布消息
127.0.0.1:6379> PUBLISH news.world "Hello, world!"
(integer) 1# 订阅者接收到消息
1) "pmessage"
2) "news.*"
3) "news.world"
4) "Hello, world!"

基于模式的发布/订阅与基于频道的发布/订阅实现原理类似,只是在订阅时可以使用通配符(*)匹配多个频道,从而实现更加灵活的消息过滤和订阅。

具体来说,当客户端通过 PSUBSCRIBE 命令订阅某个模式时,Redis 服务器会将该客户端的监听器添加到所有与该模式匹配的频道相关的事件处理器中,并在事件循环器中注册该事件处理器。当发布者通过 PUBLISH 命令向与匹配该模式的频道发送消息时,Redis 服务器会将消息发送给与该模式相关的事件处理器中的所有监听器,从而实现基于模式的消息发布和订阅。

2. Redis 发布订阅实际应用

2.1 Redis Sentinel

Redis Sentinel 是 Redis 的一套高可用方案,在主节点故障时可以自动将从节点提升为主节点,从而实现故障转移。Redis Sentinel使用发布订阅机制来实现新节点的发现以及交换主节点之间的状态,并且客户端也可以通过订阅特定频道来获取主节点故障转移的状态信息。

在 Redis Sentinel 中,每个 Sentinel 节点都会定期向 sentinel:hello 频道发送消息,并且每个 Sentinel 节点也都会订阅这个频道,这样一旦有节点往这个频道发送消息,其他节点就可以立刻收到消息,并且将该节点加入本地节点列表。此外,每次往这个频道发送消息时,可以包含节点的状态信息,作为后续 Sentinel 领导者选举的依据。

对于客户端来说,可以通过订阅 +switch-master 频道来获取主节点故障转移的状态信息。一旦 Redis Sentinel 完成了主节点故障转移,就会发布主节点的消息,客户端可以接收到该消息并及时切换到新的主节点上,从而保证系统的可靠性和可用性。
在这里插入图片描述

Redis Sentinel 节点主要使用发布订阅机制,实现新节点的发现,以及交换主节点的之间的状态。 如上图所示,每一个 Sentinel 节点将会定时向 sentinel:hello 频道发送消息,并且每个 Sentinel 都会订阅这个节点。 这样一旦有节点往这个频道发送消息,其他节点就可以立刻收到消息。
这样一旦有的新节点加入,它往这个频道发送消息,其他节点收到之后,判断本地列表并没有这个节点,于是就可以当做新的节点加入本地节点列表。

除此之外,每次往这个频道发送消息内容可以包含节点的状态信息,这样可以作为后面 Sentinel 领导者选举的依据。

以上都是对于 Redis 服务端来讲,对于客户端来讲,我们也可以用到发布订阅机制。
当 Redis Sentinel 进行主节点故障转移,这个过程各个阶段会通过发布订阅对外提供。
对于我们客户端来讲,比较关心切换之后的主节点,这样我们及时切换主节点的连接(旧节点此时已故障,不能再接受操作指令),
客户端可以订阅 +switch-master频道,一旦 Redis Sentinel 结束了对主节点的故障转移就会发布主节点的的消息。

2.1 SpringBoot Redis发布/订阅

在 Spring Boot 中,可以通过 Spring Data Redis 提供的 RedisMessageListenerContainer 和 RedisTemplate 类来实现 Redis 的发布/订阅功能。具体步骤如下:

  1. 添加 Redis 和 Spring Data Redis 的依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置 Redis 连接信息:
spring.redis.host=localhost
spring.redis.port=6379
  1. 创建 RedisMessageListenerContainer 和 RedisTemplate 实例:
@Configuration
public class RedisConfig {@Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Beanpublic RedisMessageListenerContainer messageListenerContainer() {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(redisConnectionFactory);return container;}@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);template.setDefaultSerializer(new StringRedisSerializer());return template;}
}
  1. 创建消息监听器:
@Component
public class MessageListener implements MessageListenerAdapter {@Overridepublic void onMessage(Message message, byte[] pattern) {String msg = (String) redisTemplate().getValueSerializer().deserialize(message.getBody());System.out.println("Received message: " + msg);}
}
  1. 订阅消息:
@Autowired
private RedisMessageListenerContainer container;@Autowired
private MessageListener listener;@PostConstruct
public void subscribe() {container.addMessageListener(listener, new PatternTopic("news.*"));
}
  1. 发布消息:
@Autowired
private RedisTemplate<String, Object> redisTemplate;public void publish() {redisTemplate.convertAndSend("news.world", "Hello, world!");
}

在使用 Redis 发布/订阅模式时,需要考虑订阅者的并发处理能力、消息序列化和反序列化等问题,以保证系统的可靠性和性能。

3. Redis从入门到精通系列文章

《Redis从入门到精通【进阶篇】之持久化 AOF详解》
《Redis从入门到精通【进阶篇】之持久化RDB详解》
《Redis从入门到精通【高阶篇】之底层数据结构字典(Dictionary)详解》
《Redis从入门到精通【高阶篇】之底层数据结构快表QuickList详解》
《Redis从入门到精通【高阶篇】之底层数据结构简单动态字符串(SDS)详解》
《Redis从入门到精通【高阶篇】之底层数据结构压缩列表(ZipList)详解》
《Redis从入门到精通【进阶篇】之数据类型Stream详解和使用示例》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/13584.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端Vue自定义轮播图视频播放组件 仿京东商品详情轮播图视频Video播放效果 可图片预览

前端Vue自定义轮播图视频播放组件 仿京东商品详情轮播图视频Video播放 &#xff0c;可图片预览&#xff0c;下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13325 效果图如下: # cc-videoSwiper #### 使用方法 使用方法 <!-- g…

SSM学习笔记-------Spring(一)

SSM学习笔记-------Spring&#xff08;一&#xff09; Spring_day011、课程介绍1.1 为什么要学?1.2 学什么?1.3 怎么学? 2、Spring相关概念2.1 初识Spring2.1.1 Spring家族2.1.2 了解Spring发展史 2.2 Spring系统架构2.2.1 系统架构图2.2.2 课程学习路线 2.3 Spring核心概念…

Mac如何在终端使用diskutil命令装载和卸载推出外接硬盘

最近用 macOS 装载外接硬盘的时候&#xff0c;使用mount死活装不上&#xff0c;很多文章也没详细的讲各种情况&#xff0c;所以就写一篇博客来记录一下。 如何装载和卸载硬盘&#xff08;或者说分区&#xff09; mount和umount是在 macOS 上是不能用的&#xff0c;如果使用会…

2023亚马逊云科技中国峰会——Amazon DeepRacer

1.DeepRacer技术背景 早在20世纪初汽车问世之时&#xff0c;发明家们便已提出无人驾驶的设想。但即便是实现无人驾驶的初级阶段&#xff0c;也经历了足足百年时间。毕竟在复杂的城市路况下&#xff0c;机器若想像人一样实现感知、决策、控制等功能&#xff0c;必定面临各种复杂…

无线蓝牙耳机什么牌子好?八大真无线蓝牙耳机排名

蓝牙耳机作为当前热门的数码产品&#xff0c;无论何时都能用上&#xff0c;蓝牙耳机的快速发展逐渐的取代有线耳机&#xff0c;摆脱线条的束缚&#xff0c;更方便携带。当然&#xff0c;随着蓝牙耳机的设计各种各样&#xff0c;导致很多的小伙伴在选购耳机的时候&#xff0c;不…

如何用python编写3D游戏

Vizard是一款虚拟现实开发平台软件&#xff0c;从开发至今已走过十个年头。它基于C/C&#xff0c;运用新近OpenGL拓展模块开发出的高性能图形引擎。当运用Python语言执行开发时&#xff0c;Vizard同时自动将编写的程式转换为字节码抽象层(LAXMI)&#xff0c;进而运行渲染核心。…

adb连接安卓模拟器或真机hook参数加密详细过程(frida)

app逆向时&#xff0c;参数与函数的确定很关键&#xff0c;找到可疑的函数&#xff0c;不确定是否由该函数生成&#xff0c;该怎么解决&#xff1f;hook就应允而生了&#xff0c;首先是要求本地电脑和安卓模拟器&#xff08;网易mumu模拟器支持多系统&#xff0c;该模拟器作为主…

从OVF矢量场文件中获取磁斯格明子的位置和半径的粗略方法(trace skyrmion)

文章目录 前言一、使用oommf的avf2odt命令行程序获取斯格明子中心位置的示例二、当磁体系的单个xy平面层仅有一个斯格明子的情况1.读取所有磁化文件中的指定磁化分量2.筛选出每一个xy平面层中位于磁化分量阈值范围内的单元格3.计算组成磁结构的所有单元格的平均坐标和平均距离 …

Vue:Elemenu-Plus递归型菜单组件封装

前端开发中&#xff0c;经常遇到需要与后端配置&#xff0c;前端动态渲染菜单的应用场景&#xff0c;而究其本质&#xff0c;就是菜单组件的应用&#xff0c;只是在不确定菜单级数的情况下&#xff0c;我们需要对组件做一个递归处理&#xff0c;让它能够适应大多数应用场景。 递…

支持向量机SVM代码详解——多分类/降维可视化/参数优化【python】

篇1&#xff1a;SVM原理及多分类python代码实例讲解&#xff08;鸢尾花数据&#xff09; SVM原理 支持向量机&#xff08;Support Vector Machine,SVM&#xff09;&#xff0c;主要用于小样本下的二分类、多分类以及回归分析&#xff0c;是一种有监督学习的算法。基本思想是寻…

flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单

flutter聊天界面-聊天气泡长按弹出复制、删除按钮菜单 在之前实现了flutter聊天界面的富文本展示内容&#xff0c;这里记录一下当长按聊天气泡的时候弹出复制、删除等菜单功能 一、查看效果 当长按聊天气泡的时候弹出复制、删除等菜单&#xff0c;可新增更多按钮 二、代码实现…

leetcode 222. 完全二叉树的节点个数

2023.7.3 用层序遍历遍历一遍二叉树&#xff0c;然后遍历的每个节点都进行一次计数&#xff0c;直接上代码&#xff1a; class Solution { public:int countNodes(TreeNode* root) {queue<TreeNode*> que;int ans 0;if(root nullptr) return ans;que.push(root);while…