Redis发布与订阅

什么是发布与订阅

: redis发布订阅是一种消息通信通信模式,由发送者(pub)发送消息,订阅者(sub)接收消息。

如下图client2、4、5就是订阅着,订阅了channel1的消息。

在这里插入图片描述

channel1要发送消息时,这几个订阅者都会实时收到消息。

在这里插入图片描述

发布订阅的方式有哪几种知道吗?

答: 有两种,一种基于频道的,还有一种是基于匹配的模式:

基于频道的订阅案例

指令如下所示,可以看到订阅者可以订阅多个频道

subscribe channel [channel ...]

所以我们开启一个redis客户端,订阅一个channel:sport的频道

# 客户端1 订阅 channel:sport
127.0.0.1:6379> SUBSCRIBE channel:sport
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:sport"
3) (integer) 1


发布消息的指令格式为:

publish channel message

此时,我们再开启一个redis客户端,发布一条消息到channel:sport的频道

# 另一个客户端发送消息
127.0.0.1:6379> PUBLISH channel:sport "this is why we play"
(integer) 1
127.0.0.1:6379>

此时刚刚订阅消息的redis客户端就会实时的收到这条消息

127.0.0.1:6379> SUBSCRIBE channel:sport
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:sport"
3) (integer) 1

# 如下便是实时收到的消息内容
1) "message"
2) "channel:sport" #频道
3) "this is why we play" # 消息内容

基于匹配模式的发布与订阅示例

有时候我们会订阅多个频道,我们不可能每次都去手动增加订阅的频道,例如我们当前订阅频道有:c1、c2、c3、c4、c5、c6、c7、c8。将来还可能出现c9等情况。我们不可能实时去添加订阅的频道。
观察上面的频道我们发现频道都是以c开头,后续的数字不断变化,所以我们完全可以使用模式匹配来实现频道订阅。

模式订阅和取消的命令为

psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]

关于parttern常见的匹配符有

1. *:表示任意占位符,例如c*,可以匹配c、c1、c111
2. ?*:匹配一个及以上个占位符
3. ?:表示匹配一个占位符

我们希望订阅c1-c9的频道基于模式匹配我们就能够做到这一点

我们首先开启一个客户端,使用模式匹配发起订阅

# 订阅匹配cxx相关的模式
PSUBSCRIBE c?*

然后我们在开启另一个客户端,发送消息到c1频道

127.0.0.1:6379> PUBLISH c1 "this is c1 message"
(integer) 1
127.0.0.1:6379>


刚刚订阅的客户端就会收到消息

127.0.0.1:6379> PSUBSCRIBE c?*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "c?*"
3) (integer) 1
1) "pmessage"
2) "c?*"
3) "c1"
4) "this is c1 message"

注意:当你订阅PSUBSCRIBE c?* c1订阅时,若另一个客户端发送消息到c1你会收到两条消息(原因会在后文源码解析时补充)。

如下便是PSUBSCRIBE c?* c1的收到PUBLISH c1 "this is c1 message"的消息内容

127.0.0.1:6379> PSUBSCRIBE c?* c1
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "c?*"
3) (integer) 1
1) "psubscribe"
2) "c1"
3) (integer) 2
1) "pmessage"
2) "c1"
3) "c1"
4) "this is c1 message"
1) "pmessage"
2) "c?*"
3) "c1"
4) "this is c1 message"

发布订阅的常见的使用场景和优缺点了解过嘛?

答: 使用场景如:聊天室、公告牌等需要实现消息解耦的场景都可以使用消息订阅发布,如下便是一个基于redis实现视频订阅的管理系统。

在这里插入图片描述

而优缺点也很明显:

缺点: 相较于KafkaRocketMQRedis的实现略显粗糙,无法实现消息堆积与回溯
优点: 实现简单,若不需要消息回溯等需求使用redis绰绰有余

redis是如何实现频道、模式发布与订阅的知道嘛?

答: 我们可以从源码的角度分析分析频道模式的发布与订阅:

频道发布订阅是基于字典这个数据结果,以频道作为key,用链表作为value存储所有订阅该频道的当订阅者,如下图,当channel1有消息时,程序就通过channel1订阅链,逐个给订阅者发送消息。

在这里插入图片描述

当如果客户端 client10086 执行命令 SUBSCRIBE channel1 channel2 channel3时,程序就会将这个订阅者挂到对应三个频道中。
同理,订阅者通过UNSUBSCRIBE 退订频道,程序也是通过pubsub_channels找到对应的链表将这个节点移除。

在这里插入图片描述

说完了频道的,我们再来说说redis匹配模式发布与订阅:

与频道订阅有所不同,匹配模式订阅数据结构如下所示,可以看到client123、client256订阅了tweet.shop.* ,订阅者的数据结构是一个记录客户端以及客户端订阅的模式频道的结构体,从源码我们可以看到这个结构体是这样的:

typedef struct pubsubPattern {
    redisClient *client; //指向订阅者
    robj *pattern;//指向匹配模式
} pubsubPattern;

在这里插入图片描述

从上图我们也可以看到redisServer 就是记录一个个客户端匹配模式的结构体,从源码我们就可以看到,它包含了一个pubsub_patterns的指针用于记录每个模式订阅者

struct redisServer {
    // ...
    list *pubsub_patterns;
    // ...
};

例如client10086 订阅broadcast.live.*,程序就会在redisServer所指向的链表中挂一个新的pubsub_patterns ,如下图所示:

在这里插入图片描述

基于spring boot集成redis演示发布与订阅

需求

我们希望一个用户订阅channel:sport,一个用户订阅channel:stock,而另一个用户两个都订阅

实现

首先pom配置引入相关依赖

<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

spring boot的application.yml配置redis配置

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password:
    database: 0 

接收redis消息行为接口

package com.example.demo;

import org.springframework.stereotype.Component;

/**
 * 发布消息接口类
 */
@Component
public interface RedisMsg {

     void receiveMessage(String message);
}

三个订阅者

/**
 * 订阅所有channel:*的消息
 */
public class RedisSubPChannel implements RedisMsg {


    public void receiveMessage(String message) {
        System.out.println("RedisSubPChannel收到消息消息" + message);
    }
}
package com.example.demo;


/**
 * 订阅所有channel:sport最新消息
 */
public class RedisSubSport implements RedisMsg{


    public void receiveMessage(String message){

        System.out.println("RedisSubSport获取体育频道最新消息:"+message);
    }
}

package com.example.demo;

/**
 * 订阅所有channel:stock消息
 */
public class RedisSubStock implements RedisMsg {



    @Override
    public void receiveMessage(String message) {
        System.out.println("RedisSubStock获取channel:stock最新消息:"+message);
    }
}

redis配置类

@Configuration
@EnableCaching
public class RedisConfig{
    /**
     * Redis消息监听器容器
     * @param connectionFactory
     * @return
     */
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //订阅三个频道
        container.addMessageListener(listenerAdapter(new RedisSubSport()),new PatternTopic("channel:sport"));
        container.addMessageListener(listenerAdapter(new RedisSubStock()),new PatternTopic("channel:stock"));
        container.addMessageListener(listenerAdapter(new RedisSubPChannel()),new PatternTopic("channel:*"));
        return container;
    }

    /**
     * 配置消息接收处理类
     * @param redisMsg  自定义消息接收类
     * @return
     */
    @Bean()
    @Scope("prototype")
    MessageListenerAdapter listenerAdapter(RedisMsg redisMsg) {
        //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
        //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
        return new MessageListenerAdapter(redisMsg, "receiveMessage");//注意2个通道调用的方法都要为receiveMessage
    }

}

消息发送者

//定时器
@EnableScheduling
@Component
public class SenderController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //向redis消息队列index通道发布消息
    @Scheduled(fixedRate = 2000)
    public void sendMessage() {
        stringRedisTemplate.convertAndSend("channel:stock", "股票涨了"+Math.random()+"个百分点");
        stringRedisTemplate.convertAndSend("channel:sport",  "湖人队 No."+((int)(Math.random()*100)+1)+"选手得分");
    }
}

运行结果

RedisSubStock获取channel:stock最新消息:股票涨了0.46015187165884985个百分点
RedisSubPChannel收到消息消息股票涨了0.46015187165884985个百分点
RedisSubSport获取体育频道最新消息:湖人队 No.40选手得分
RedisSubPChannel收到消息消息湖人队 No.40选手得分
RedisSubStock获取channel:stock最新消息:股票涨了0.5293839028647649个百分点
RedisSubPChannel收到消息消息股票涨了0.5293839028647649个百分点
RedisSubSport获取体育频道最新消息:湖人队 No.26选手得分
RedisSubPChannel收到消息消息湖人队 No.26选手得分
RedisSubPChannel收到消息消息股票涨了0.6779431428770823个百分点
RedisSubStock获取channel:stock最新消息:股票涨了0.6779431428770823个百分点
RedisSubSport获取体育频道最新消息:湖人队 No.35选手得分
RedisSubPChannel收到消息消息湖人队 No.35选手得分
RedisSubStock获取channel:stock最新消息:股票涨了0.40332150203704个百分点
RedisSubPChannel收到消息消息股票涨了0.40332150203704个百分点
RedisSubSport获取体育频道最新消息:湖人队 No.14选手得分
RedisSubPChannel收到消息消息湖人队 No.14选手得分
RedisSubStock获取channel:stock最新消息:股票涨了0.8407461728769783个百分点
RedisSubPChannel收到消息消息股票涨了0.8407461728769783个百分点
RedisSubSport获取体育频道最新消息:湖人队 No.25选手得分
RedisSubPChannel收到消息消息湖人队 No.25选手得分

参考文献

Redis开发与运维

springboot入门–springboot集成redis实现消息发布订阅模式-双通道

Redis进阶 - 消息传递:发布订阅模式详解

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Koa.js 入门手册:洋葱模型插件机制详解以及常用中间件

前言 Nodejs 提供了 http 能力&#xff0c;我们通过如下代码可以快速创建一个http server服务 const http require(http);http.createServer((req, res) > {res.write(hello\n);res.end();}).listen(3000);使用nodejs提供的原生能力启动一个http server并不麻烦&#xff…

gun-fight枪战对决游戏(自创)

前言 好久都没有更新过啦&#xff01; 游戏介绍 这是一款枪战游戏&#xff0c;你将和人机对战&#xff0c;在火线中对决&#xff01;具体是怎么样的快下载试试吧&#xff01; 下载链接 文件 密码是1111 后言 点个赞吧&#xff01;

阿里云主导《Serverless 计算安全指南》国际标准正式立项!

日前&#xff0c;在韩国召开的国际电信联盟电信标准分局 ITU-T SG17 全会上&#xff0c;由阿里云主导的《Serverless 计算安全指南》国际标准正式立项成功。 图 1 项目信息 在现今数字化时代&#xff0c;Serverless 计算正逐渐成为云计算的一个新的发展方向&#xff0c;其灵活…

智能优化算法应用:基于世界杯算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于世界杯算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于世界杯算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.世界杯算法4.实验参数设定5.算法结果6.参考文…

Python 爬虫之简单的爬虫(三)

爬取动态网页&#xff08;上&#xff09; 文章目录 爬取动态网页&#xff08;上&#xff09;前言一、大致内容二、基本思路三、代码编写1.引入库2.加载网页数据3.获取指定数据 总结 前言 之前的两篇写的是爬取静态网页的内容&#xff0c;比较简单。接下来呢给大家讲一下如何去…

用户行为分析遇到的问题-ubantu16,hadoop3.1.3

用户行为分析传送门 我的版本 ubantu16 hadoop 3.1.3 habse 2.2.2 hive3.1.3 zookeeper3.8.3 sqoop 1.46/1.47 我sqoop把MySQL数据往hbase导数据时候有问题 重磅&#xff1a;大数据课程实验案例&#xff1a;网站用户行为分析&#xff08;免费共享&#xff09; 用户行为分析-小…

OpenCV技术应用(8)— 如何将视频分解

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教大家如何将一幅图像转化成热力图&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍 视频是…

STM32——时钟树与滴答计时器

STM32——时钟树与滴答计时器 使用的开发板为stm32F407VET6的芯片,主要介绍stm32的时钟树与滴答计时器的一些理论和一个自己编写的delay函数。 时钟树的结构图可以在STM32F4xx中文参考手册.pdf中的时钟这块找到。而滴答计时器是内核资源&#xff0c;需要到Cortex M3与M4权威指南…

图片转excel:二种合并方式,有何区别?

图片怎么转为可编辑的excel&#xff0c;并且将转换结果合并为一个表&#xff1f;打开眼精星表格文字识别电脑客户端&#xff0c;我们可以看到顶部有一个功能&#xff0c;名为“表格合并”&#xff0c;而在表格识别模块提交选项里&#xff0c;我们会发现有“合并”选项&#xff…

Win10电脑退出安全模式的两种方法

在Win10电脑中&#xff0c;大家可以点击进入系统安全模式&#xff0c;完成相对应的系统设置。但是&#xff0c;很多用户进入安全模式完成设置后&#xff0c;不知道怎么操作才能成功退出安全模式&#xff1f;接下来小编给大家分享两种简单的方法&#xff0c;帮助大家成功退出Win…

【改进YOLOv8】磁瓦缺陷分类系统:改进LSKNet骨干网络的YOLOv8

1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 研究背景与意义 近年来&#xff0c;随着智能制造产业的不断发展&#xff0c;基于人工智能与机器视觉的自动化产品缺陷检测技术在各行各业中得到了广泛应用。磁瓦作为永磁电机的主…

JVM-6-HotSpot的算法细节实现

根节点枚举 迄今为止&#xff0c;所有收集器在根节点枚举这一步骤时都是必须暂停用户线程的&#xff0c;因此毫无疑问根节点枚举与之前提及的整理内存碎片一样会面临相似的“Stop The World”的困扰。 即使是号称停顿时间可控&#xff0c;或者&#xff08;几乎&#xff09;不…

深入解析Guava范围类(Range)

第1章&#xff1a;范围类Range的重要性 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊一个在Java编程世界里非常实用但又被低估的角色——Guava库中的Range类。你知道吗&#xff0c;在处理涉及到数值范围的问题时&#xff0c;Range类就像是咱们的救星。不论是判断某个数…

Python3中_和__的用途和区别

目录 一、_&#xff08;下划线&#xff09; 1、临时变量&#xff1a; 2、未使用的变量&#xff1a; 二、__&#xff08;双下划线&#xff09; 1、私有属性&#xff1a; 2、私有方法&#xff1a; 三、__的一些特殊用途。 总结 Python3中的_和__是两个特殊的标识符&#…

MongoDB的数据库引用

本文主要介绍MongoDB的数据库引用。 目录 MongoDB的数据库引用 MongoDB的数据库引用 MongoDB是一种面向文档的NoSQL数据库&#xff0c;它使用BSON&#xff08;Binary JSON&#xff09;格式存储和查询数据。在MongoDB中&#xff0c;数据库引用是一种特殊的数据类型&#xff0c;…

面试算法56:二叉搜索树中两个节点的值之和

题目 给定一棵二叉搜索树和一个值k&#xff0c;请判断该二叉搜索树中是否存在值之和等于k的两个节点。假设二叉搜索树中节点的值均唯一。例如&#xff0c;在如图8.12所示的二叉搜索树中&#xff0c;存在值之和等于12的两个节点&#xff08;节点5和节点7&#xff09;&#xff0…

本地缓存与多级缓存

一、前言 缓存对于一个高并发场景下的微服务应用来说具有重要的作用&#xff0c;不管是在架构选型还是设计阶段&#xff0c;缓存都是应用扛高并发提升吞吐量的有效手段。缓存对于大多数开发的同学来说并不陌生&#xff0c;一个基本的缓存使用流程如下&#xff1a; 简而言之&am…

华为配置OSPF与BFD联动示例

组网需求 如图1所示&#xff0c;SwitchA、SwitchB和SwitchC之间运行OSPF&#xff0c;SwitchA和SwitchB之间的交换机仅作透传功能。现在需要SwitchA和SwitchB能快速感应它们之间的链路状态&#xff0c;当链路SwitchA-SwitchB发生故障时&#xff0c;业务能快速切换到备份链路Swi…

【LeetCode刷题笔记(9-1)】【Python】【无重复字符的最长子串】【滑动窗口】【中等】

文章目录 引言无重复字符的最长子串题目描述提示 解决方案1&#xff1a;【滑动窗口】结束语 无重复字符的最长子串 引言 编写通过所有测试案例的代码并不简单&#xff0c;通常需要深思熟虑和理性分析。虽然这些代码能够通过所有的测试案例&#xff0c;但如果不了解代码背后的思…

七轴开源协作机械臂myArm视觉跟踪技术!

引言 ArUco标记是一种基于二维码的标记&#xff0c;可以被用于高效的场景识别和位置跟踪。这些标记的简单性和高效性使其成为机器视觉领域的理想选择&#xff0c;特别是在需要实时和高精度跟踪的场景中。结合机器学习和先进的图像处理技术&#xff0c;使用ArUco标记的机械臂系统…