默认配置notify-keyspace-events的值为" ", 表示关闭
修改为 notify-keyspace-events Ex 这样便开启了过期事件
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
@Resource
private KeyExpireFactory keyExpireFactory;
@Override
public void onMessage(Message message, byte[] pattern) {
// message为任何过期key
String expiredKey = message.toString();
// 根据key的前缀 去匹配过期策略
String implCode = KeyExpireStrategyEnum.getImplByPreFix(expiredKey);
if (StringUtils.isBlank(implCode)) {
return;
}
// 根据不同策略执行不同处理方式
keyExpireFactory.getHandleService(implCode).handleExpiredKey(expiredKey);
}
public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) {
super(redisMessageListenerContainer);
}
}
重写的onMessage()方法, 入参 message 是过期的key, 但是有一点需要注意, 任何过期的key, 都会被监听器捕捉到.
只要能拿到key, 就可以针对不同的key, 做不同的处理.
// 键空间
PUBLISH __keyspace@__:mykey
PUBLISH __keyspace@*__:com.*
@后指定的是redis的库 如果不填则默认是0库
@*指定的是全库
keyspace 是根据key的name去监听, key* 表示监听以com.开头的所有key 所有动作都会监听, 比如 set, delete, expired
// 键事件
PUBLISH __keyevent@2__:del
对事件进行监听 2库所有key被删除时会触发事件
keyspace监听到的message内容是key的事件
keyevent监听到的message内容是key的名称
"__keyspace@__:mykey"
"set"
"__keyevent@2__:del"
"mykey"
@Configuration
public class RedisConfig {
@Autowired
private RedisUpdateAndAddListener redisUpdateAndAddListener;
@Autowired
private RedisDeleteListener redisDeleteListener;
@Autowired
private RedisExpiredListener redisExpiredListener;
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//监听所有的key的set事件
container.addMessageListener(redisUpdateAndAddListener, redisUpdateAndAddListener.getTopic());
//监听所有key的删除事件
container.addMessageListener(redisDeleteListener,redisDeleteListener.getTopic());
//监听所有key的过期事件
container.addMessageListener(redisExpiredListener,redisExpiredListener.getTopic());
return container;
}
}
@Component
@Data
public class RedisUpdateAndAddListener implements MessageListener {
//监听的主题
private final PatternTopic topic = new PatternTopic("__keyevent@*__:set");
@Override
public void onMessage(Message message,byte[] pattern){
String topic = new String(pattern);
String msg = new String(message.getBody());
System.out.println("收到key更新或修改,消息主题是:"+ topic+",消息内容是:"+msg);
}
}
@Component
@Data
public class RedisDeleteListener implements MessageListener {
//监听主题
private final PatternTopic topic = new PatternTopic("__keyevent@*__:del");
/**
*
* @param message 消息
* @param pattern 主题
*/
@Override
public void onMessage(Message message, byte[] pattern) {
String topic = new String(pattern);
String msg = new String(message.getBody());
System.out.println("收到key的删除,消息主题是:"+ topic+",消息内容是:"+msg);
}
}
弊端及解决办法
目前暂不支持订阅 keyspace + keyevent, keyevent会监听所有的key, 如果redis库中的key比较多, 则会耗redis资源, 且删除事件也会触发的不及时. 不及时原因可以参考redis的删除策略
可以开辟一个库, 比如库16, 专门用来存放需要被监听的key, 这样可以有效降低库中key的总数
redis的三种删除策略
1.定时删除: key带有ttl, redis会开启定时器, 定时删除. 准确性高但是耗费性能
2.定期删除: 以某个频率去删除过期的key, 比如一次扫20个key, 删除其中已过期的. 如有过期的超过1/4, 则会再次扫描, 知道小于阈值.这些参数都可以在redis配置中修改
3.惰性删除: key过期了不会主动删除, 直到被访问时才删除. 如果一直不被访问, 就一直占用空间无法删除
redis默认采用的过期策略是 定期删除 + 惰性删除