SpringBoot如何监控Redis中某个Key的变化(自定义监听器)

目录
  • SpringBoot 监控Redis中某个Key的变化
    • 1.声明
    • 2.基本理念
    • 3.实现和创建监听
    • 4.基本demo的其他配置
    • 5.基本测试
    • 6.小结一下
  • SpringBoot自定义监听器
    • 原理
    • 示例

SpringBoot 监控Redis中某个Key的变化

1.声明

当前内容主要为本人学习和基本测试,主要为监控redis中的某个key的变化(感觉网上的都不好,所以自己看Spring源码直接写一个监听器)

个人参考:

2.基本理念

网上的demo的缺点

  • 使用继承KeyExpirationEventMessageListener只能监听当前key消失的事件
  • 使用KeyspaceEventMessageListener只能监听所有的key事件

总体来说,不能监听某个特定的key的变化(某个特定的redis数据库),具有缺陷

直接分析获取可以操作的步骤

查看KeyspaceEventMessageListener的源码解决问题

基本思想

  • 创建自己的主题(用来监听某个特定的key)
  • 创建监听器实现MessageListener
  • 注入自己的配置信息

查看其中的方法(init方法)

public void init() {
		if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {
			RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();
			try {
				Properties config = connection.getConfig("notify-keyspace-events");
				if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
					connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
				}
			} finally {
				connection.close();
			}
		}
		doRegister(listenerContainer);
	}
	/**
	 * Register instance within the container.
	 *
	 * @param container never {@literal null}.
	 */
	protected void doRegister(RedisMessageListenerContainer container) {
		listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
	}

主要操作如下

  • 向redis中写入配置notify-keyspace-events并设置为EA
  • 向RedisMessageListenerContainer中添加本身这个监听器并指定监听主题

所以本人缺少的就是这个主题表达式和监听的notify-keyspace-events配置

直接来到redis的官方文档找到如下内容

所以直接选择的是:__keyspace@0__:myKey,使用的模式为KEA

所有的工作全部完毕后开始实现监听

3.实现和创建监听

创建监听类:RedisKeyChangeListener

本类中主要监听redis中数据库0的myKey这个key

import java.nio.charset.Charset;
import java.util.Properties;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.listener.KeyspaceEventMessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import org.springframework.util.StringUtils;
/**
 *
 * @author hy
 * @createTime 2021-05-01 08:53:19
 * @description 期望是可以监听某个key的变化,而不是失效
 *
 */
public class RedisKeyChangeListener implements MessageListener/* extends KeyspaceEventMessageListener */ {
	private final String listenerKeyName; // 监听的key的名称
	private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*"); //表示只监听所有的key
	private static final Topic TOPIC_KEYEVENTS_SET = new PatternTopic("__keyevent@0__:set"); //表示只监听所有的key
	private static final Topic TOPIC_KEYNAMESPACE_NAME = new PatternTopic("__keyspace@0__:myKey"); // 不生效
	// 监控
	//private static final Topic TOPIC_KEYEVENTS_NAME_SET_USELESS = new PatternTopic("__keyevent@0__:set myKey");
	private String keyspaceNotificationsConfigParameter = "KEA";
	public RedisKeyChangeListener(RedisMessageListenerContainer listenerContainer, String listenerKeyName) {
		this.listenerKeyName = listenerKeyName;
		initAndSetRedisConfig(listenerContainer);
	}
	public void initAndSetRedisConfig(RedisMessageListenerContainer listenerContainer) {
		if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {
			RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();
			try {
				Properties config = connection.getConfig("notify-keyspace-events");
				if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
					connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
				}
			} finally {
				connection.close();
			}
		}
		// 注册消息监听
		listenerContainer.addMessageListener(this, TOPIC_KEYNAMESPACE_NAME);
	}
	@Override
	public void onMessage(Message message, byte[] pattern) {
		System.out.println("key发生变化===》" + message);
		byte[] body = message.getBody();
		String string = new String(body, Charset.forName("utf-8"));
		System.out.println(string);
	}
}

其实就改了几个地方…

4.基本demo的其他配置

1.RedisConfig配置类

@Configuration
@PropertySource(value = "redis.properties")
@ConditionalOnClass({ RedisConnectionFactory.class, RedisTemplate.class })
public class RedisConfig {
	@Autowired
	RedisProperties redisProperties;
	/**
	 *
	 * @author hy
	 * @createTime 2021-05-01 08:40:59
	 * @description 基本的redisPoolConfig
	 * @return
	 *
	 */
	private JedisPoolConfig jedisPoolConfig() {
		JedisPoolConfig config = new JedisPoolConfig();
		config.setMaxIdle(redisProperties.getMaxIdle());
		config.setMaxTotal(redisProperties.getMaxTotal());
		config.setMaxWaitMillis(redisProperties.getMaxWaitMillis());
		config.setTestOnBorrow(redisProperties.getTestOnBorrow());
		return config;
	}
	/**
	 * @description 创建redis连接工厂
	 */
	@SuppressWarnings("deprecation")
	private JedisConnectionFactory jedisConnectionFactory() {
		JedisConnectionFactory factory = new JedisConnectionFactory(
				new JedisShardInfo(redisProperties.getHost(), redisProperties.getPort()));
		factory.setPassword(redisProperties.getPassword());
		factory.setTimeout(redisProperties.getTimeout());
		factory.setPoolConfig(jedisPoolConfig());
		factory.setUsePool(redisProperties.getUsePool());
		factory.setDatabase(redisProperties.getDatabase());
		return factory;
	}
	/**
	 * @description 创建RedisTemplate 的操作类
	 */
	@Bean
	public StringRedisTemplate getRedisTemplate() {
		StringRedisTemplate redisTemplate = new StringRedisTemplate();
		redisTemplate.setConnectionFactory(jedisConnectionFactory());
		redisTemplate.setEnableTransactionSupport(true);
		return redisTemplate;
	}

	@Bean
	public RedisMessageListenerContainer redisMessageListenerContainer() throws Exception {
		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
		container.setConnectionFactory(jedisConnectionFactory());
		return container;
	}
	// 创建基本的key监听器
	/*  */
	@Bean
	public RedisKeyChangeListener redisKeyChangeListener() throws Exception {
		RedisKeyChangeListener listener = new RedisKeyChangeListener(redisMessageListenerContainer(),"");
		return listener;
	}
}

其中最重要的就是RedisMessageListenerContainer 和RedisKeyChangeListener

2.另外的RedisProperties类,加载redis.properties文件成为对象的

/**
 *
 * @author hy
 * @createTime 2021-05-01 08:38:26
 * @description 基本的redis的配置类
 *
 */
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
	private String host;
	private Integer port;
	private Integer database;
	private Integer timeout;
	private String password;
	private Boolean usePool;
	private Integer maxTotal;
	private Integer maxIdle;
	private Long maxWaitMillis;
	private Boolean testOnBorrow;
	private Boolean testWhileIdle;
	private Integer timeBetweenEvictionRunsMillis;
	private Integer numTestsPerEvictionRun;
	// 省略get\set方法
}

省略其他代码

5.基本测试

创建一个key,并修改发现变化

可以发现返回的是这个key执行的方法(set),如果使用的是keyevent方式那么返回的就是这个key的名称

6.小结一下

1.监听redis中的key的变化主要利用redis的机制来实现(本身就是发布/订阅)

2.默认情况下是不开启的,原因有点耗cpu

3.实现的时候需要查看redis官方文档和SpringBoot的源码来解决实际的问题

SpringBoot自定义监听器

原理

Listener按照监听的对象的不同可以划分为:

  • 监听ServletContext的事件监听器,分别为:ServletContextListener、ServletContextAttributeListener。Application级别,整个应用只存在一个,可以进行全局配置。
  • 监听HttpSeesion的事件监听器,分别为:HttpSessionListener、HttpSessionAttributeListener。Session级别,针对每一个对象,如统计会话总数。
  • 监听ServletRequest的事件监听器,分别为:ServletRequestListener、ServletRequestAttributeListener。Request级别,针对每一个客户请求。

示例

第一步:创建项目,添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.eclipse.jdt.core.compiler</groupId>
    <artifactId>ecj</artifactId>
    <version>4.6.1</version>
</dependency>

第二步:自定义监听器

@WebListener
public class MyServletRequestListener implements ServletRequestListener {
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("Request监听器,销毁");
    }
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("Request监听器,初始化");
    }
}

第三步:定义Controller

@RestController
public class DemoController {
    @RequestMapping("/fun")
    public void fun(){
        System.out.println("fun");
    }
}

第四步:在程序执行入口类上面添加注解

@ServletComponentScan 

部署项目,运行查看效果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • springboot+redis过期事件监听实现过程解析

    1 修改 redis.conf配置文件: K Keyspace events, published with keyspace@ prefix事件 E Keyevent events, published with keyevent@ prefix g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, - $ String commands l List commands s Set commands h Hash co

  • SpringBoot如何整合redis实现过期key监听事件

    可以用于简单的过期订单取消支付.7天自动收货场景中 1.Spring Boot整合redis 参考 https://www.jb51.net/article/170687.htm 2.打开redis服务的配置文件添加notify-keyspace-events Ex 如果是注释了,就取消注释 Linux安装redis:https://www.jb51.net/article/193265.htm Windows安装redis:https://www.jb51.net/article/176040

  • spring boot+redis 监听过期Key的操作方法

    前言: 在订单业务中,有时候需要对订单设置有效期,有效期到了后如果还未支付,就需要修改订单状态.对于这种业务的实现,有多种不同的办法,比如: 1.使用querytz,每次生成一个订单,就创建一个定时任务,到期后执行业务代码: 2.rabbitMq中的延迟队列: 3.对Redis的Key进行监控: 1.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring

  • Spring Boot监听Redis Key失效事件实现定时任务的示例

    业务场景 我们以订单功能为例说明下: 生成订单后一段时间不支付订单会自动关闭.最简单的想法是设置定时任务轮询,但是每个订单的创建时间不一样,定时任务的规则无法设定,如果将定时任务执行的间隔设置的过短,太影响效率. 还有一种想法,在用户进入订单界面的时候,判断时间执行相关操作.方式可能有很多,在这里介绍一种监听 Redis 键值对过期时间来实现订单自动关闭. 实现思路 在生成订单时,向 Redis 中增加一个 KV 键值对,K 为订单号,保证通过 K 能定位到数据库中的某个订单即可,V 可为任意值

  • Spring boot事件监听实现过程解析

    事件监听其实我们并不陌生,简单来讲,当程序达到了某个特定的条件,程序就会自动执行一段指令.在spring 中也一样,我们可以使用spring中的事件监听来实现某些特定的需求. 发布事件 既然要监听事件,首先要发布我们的事件嘛.在spring中发布事件我们可以通过继承ApplicationEvent 来发布我们的事件类. @Data public class SendEvent extends ApplicationEvent { public SendEvent(Object source) {

  • SpringBoot如何监控Redis中某个Key的变化(自定义监听器)

    目录 SpringBoot 监控Redis中某个Key的变化 1.声明 2.基本理念 3.实现和创建监听 4.基本demo的其他配置 5.基本测试 6.小结一下 SpringBoot自定义监听器 原理 示例 SpringBoot 监控Redis中某个Key的变化 1.声明 当前内容主要为本人学习和基本测试,主要为监控redis中的某个key的变化(感觉网上的都不好,所以自己看Spring源码直接写一个监听器) 个人参考: Redis官方文档 Spring-data-Redis源码 2.基本理念

  • vue之组件内监控$store中定义变量的变化详解

    // 1.采用计算属性来获取$store中的值 computed: { listenstage() { return this.$store.state.iShaveMsg; } }, // 2.通过watch来检查定义计算属性获取到的值的变化 watch:{ listenstage: function(ov,nv){ console.log('watch start--'); if(this.$store.state.iShaveMsg){ //业务处理 } } console.log('wa

  • redis中热key问题该如何解决

    引言 讲了几天的数据库系列的文章,大家一定看烦了,其实还没讲完...(以下省略一万字). 今天我们换换口味,来写redis方面的内容,谈谈热key问题如何解决. 其实热key问题说来也很简单,就是瞬间有几十万的请求去访问redis上某个固定的key,从而压垮缓存服务的情情况. 其实生活中也是有不少这样的例子.比如XX明星结婚.那么关于XX明星的Key就会瞬间增大,就会出现热数据问题. ps: hot key和big key问题,大家一定要有所了解. 本文预计分为如下几个部分 热key问题 如何发

  • Redis中常见的几种集群部署方案

    目录 前言 几种常用的集群方案 主从集群模式 全量同步 增量同步 举个栗子 哨兵机制 什么是哨兵机制 如何保证选主的准确性 如何选主 选举主节点的规则 哨兵进行主节点切换 切片集群 RedisCluster方案 哈希槽重新分配 避免HotKey 如何发现HotKey HotKey如何解决 避免BigKey BigKey存在问题 如何发现BigKey BigKey如何避免 BigKey如何删除 参考 前言 这里来了解一下,Redis 中常见的集群方案 几种常用的集群方案 主从集群模式 哨兵机制 切

  • Spring boot redis cache的key的使用方法

    在数据库查询中我们往往会使用增加缓存来提高程序的性能,@Cacheable 可以方便的对数据库查询方法加缓存.本文主要来探究一下缓存使用的key. 搭建项目 数据库 mysql> select * from t_student; +----+--------+-------------+ | id | name | grade_class | +----+--------+-------------+ | 1 | Simone | 3-2 | +----+--------+-----------

  • Redis遍历所有key的两个命令(KEYS 和 SCAN)

    当我们需要遍历Redis所有key或者指定模式的key时,首先想到的是KEYS命令: KEYS pattern 官网对于KEYS命令有一个提示:  KEYS 的速度非常快,例如,Redis在一个有1百万个key的数据库里面执行一次查询需要的时间是40毫秒 .但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的  KEYS , 你最好还是用 Redis 的集合结构  SETS  来代替. KEYS命令使用很简单. redis> MSET one 1 two 2 thr

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • Redis中过期键如何删除示例详解

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

随机推荐