为什么要自定义事件监听呢?当然是为了在redis进行某些事件动作的时候增加一些咱们自己的业务逻辑处理,比如:当key失效的时候,处理业务逻辑--支付订单半小时未支付就自动取消订单。这就可以用key失效事件监听来实现。下面就来讲解怎么实现:
1.首先自定义一个消息工厂
此处不要自己再重新获取连接自定义连接池。直接使用redis现有的连接池即可。
package com.liu.redisexpired; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import reactor.util.annotation.NonNull; /** * 自定义Redis键空间监听器⼯⼚ * @author kevin * @date 2022/6/7 */ @Slf4j public class RedisMessageListenerFactory implements BeanFactoryAware, ApplicationListener<ContextRefreshedEvent> { //spring bean工厂 private DefaultListableBeanFactory beanFactory; //redis连接工厂 private RedisConnectionFactory redisConnectionFactory; //redis消息监听 @Autowired private MessageListener messageListener; public void setBeanFactory(@NonNull BeanFactory beanFactory) { this.beanFactory = (DefaultListableBeanFactory) beanFactory; } public void setRedisConnectionFactory(RedisConnectionFactory redisConnectionFactory) { this.redisConnectionFactory = redisConnectionFactory; } /** * 自定义事件监听 * @author kevin * @date 2022/5/27 */ @Override public void onApplicationEvent(@NonNull ContextRefreshedEvent contextRefreshedEvent) { //定义一个单例bean,设置连接工厂为redis的连接工厂,然后注册到spring容器 BeanDefinitionBuilder containerBeanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(RedisMessageListenerContainer.class); containerBeanDefinitionBuilder.addPropertyValue("connectionFactory", redisConnectionFactory); containerBeanDefinitionBuilder.setScope(BeanDefinition.SCOPE_SINGLETON); containerBeanDefinitionBuilder.setLazyInit(false); beanFactory.registerBeanDefinition("myRedisKeyListenerContainer", containerBeanDefinitionBuilder.getRawBeanDefinition()); //从spring工厂容器获取消息监听容器,配置消息监听器 RedisMessageListenerContainer container = beanFactory.getBean("myRedisKeyListenerContainer", RedisMessageListenerContainer.class); //设置监听正则,监听节点所有key的所有事件 container.addMessageListener(messageListener, new PatternTopic("__keyevent@*__:*")); container.afterPropertiesSet(); //开始监听 container.start(); } }2.将消息工厂注入spring容器
在你的redisconfig配置中加入代码,主要看最后一个方法:
package com.liu.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import com.liu.redisexpired.RedisMessageListenerFactory; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { /** * 配置第一个数据源的RedisTemplate * 注意:这里指定使用名称=factory 的 RedisConnectionFactory * 并且标识第一个数据源是默认数据源 @Primary * @author kevin * @param redisConnectionFactory : * @return org.springframework.data.redis.core.RedisTemplate * @date 2022/5/26 */ @Bean("redisTemplate") @Primary public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { return getRedisTemplate(redisConnectionFactory); } /** * redis操作工具获取redisTemplate * @author kevin * @param factory : redis连接工厂 * @return org.springframework.data.redis.core.RedisTemplate<java.lang.String,java.lang.Object> * @date 2022/5/27 12:00 */ private RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); // om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL) //过期方法 om.activateDefaultTyping( LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } /** * 自定义redis消息监听工厂 * @author kevin * @date 2022/5/27 */ @Bean public RedisMessageListenerFactory redisMessageListenerFactory( BeanFactory beanFactory, RedisConnectionFactory redisConnectionFactory) { RedisMessageListenerFactory beans = new RedisMessageListenerFactory(); beans.setBeanFactory(beanFactory); beans.setRedisConnectionFactory(redisConnectionFactory); return beans; } }3.编写消息监听:
编写一个监听器,实现MessageListener接口,实现onMessage方法,得到的message就可以监听所有的key的事件了。此处只监听了key的过期事件,然后打印了一句日志,如果需要其他操作,可直接在此处改写即可
package com.liu.redisexpired; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.stereotype.Component; /** * 事件触发监听器 * @author kevin * @date 2022/5/27 */ @Slf4j @Component public class KeyEventMessageListener implements MessageListener { /** * 监听获取redis键消息 * @author kevin * @param message : 事件消息体 * @param pattern : 事件监听,消息主题(订阅频道),如: * __keyevent@*__:* 所有key的所有事件 __keyevent@a__:* key为a的所有事件 * __keyevent@a__:expired key为a的key过期事件 * @date 2022/5/27 11:05 */ @Override public void onMessage(Message message, byte[] pattern) { //获取redis操作类型: set、expire、expired等操作 String action = new String(message.getChannel()); action = action.split(":")[1]; //获取redis操作的key String key = new String(message.getBody()); //判断失效的key,且是请求限流的key if("expired".equals(action) && key.startsWith("request_limit_")){ log.info("监听到的键:{},已经失效,请求限流已取消!", key); } } }此处作者监听的是以request_limit_开头的键失效事件(做接口限流的时候,顺便整了一下键事件监听)。
4. 然后验证:
启动你的项目,然后使用你的项目设置一个键并设置失效事件;
或者直接连接redis命令行,先通过如下命令连接redis命令行:
redis-cli -h 127.0.0.1 -p 6379 -a xxx在命令行中执行命令添加一个键值,并设置过期时间为2秒:
set request_limit_test 1 PX 2000然后查看项目的控制台是否有对应的输出:
以上就是所有内容