在一个需求中,需要当redis的key删除或过期的时候往日志表中插入一条对应的记录。那么如何监听redis的过期key呢?
1.首先需要设置redis配置文件
notify-keyspace-events Ex,
或者使用命令 CONFIG set notify-keyspace-events Ex。
那么notify-keyspace-events 后面都有哪些参数呢
输入的参数中至少要有一个K或者E,否则其余参数不会有任何的通知生效。
2.springboot中配置redis
@Configuration
public class RedisConfig {
@Bean
@Primary
public <T> RedisTemplate<String, T> getRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer());
return redisTemplate;
}
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericFastJsonRedisSerializer());
return redisTemplate;
}
@Bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
return stringRedisTemplate;
}
@Bean
public RedisScript<Boolean> hitMaxScript() {
DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/hitmax.lua")));
redisScript.setResultType(Boolean.class);
return redisScript;
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory);
container.setTaskExecutor(executor());
Topic topic = new PatternTopic(RedisKeyExpirationListener.LISTENER_PATTERN);
container.addMessageListener(new RedisKeyExpirationListener(), topic);
return container;
}
@Bean
public Executor executor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("V-Thread");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
3.自定义一个监听器重写对应的onMessage方法
@Component
public class RedisKeyExpirationListener implements MessageListener {
public static final String LISTENER_PATTERN = "__key*@*__:*";
@Resource
private OperationLimitService operationLimitService;
@Override
public void onMessage(Message message, byte[] pattern) {
System.err.println("触发监听器。。。。。。");
String body = new String(message.getBody());
String channel = new String(message.getChannel());
System.out.println("onMessage >> "+String.format("channel: %s, body: %s, bytes: %s",channel,body,new String(pattern)));
}
}
但上面的service注入的是一个null,猜测原因是监听器的执行早于spring的注入。所以又增加了获取bean的代码
@Slf4j
@Configuration
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
log.debug("从SpringContextHolder中取出Bean:" + name);
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
log.info("清除SpringContextHolder中的ApplicationContext:"
+ applicationContext);
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
// logger.debug("注入ApplicationContext到SpringContextHolder:{}", applicationContext);
if (SpringContextHolder.applicationContext != null) {
log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
}
SpringContextHolder.applicationContext = applicationContext; // NOSONAR
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
if(applicationContext == null) {
throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
}
}
}
然后这个bean直接从applicationContext中获取就行了