基础知识
`redis自2.8.0之后版本提供Keyspace Notifications功能,允许客户订阅Pub / Sub频道,以便以某种方式接收影响Redis数据集的事件
默认情况下,redis的通知事件是关闭的,在终端执行以下命令开启:`
其中 KEA表示启用了所有可能的事件
代码分析
springboot集成了redis,直接引包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
KeyspaceEventMessageListener 监听Key所有事件 private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*"); __keyevent@* db下所有key, __keyevent@db其中db是可以设置的
开启redis的通知事件
private String keyspaceNotificationsConfigParameter = "EA";
public void init() { Properties config = connection.getConfig("notify-keyspace-events"); connection.setConfig("notify-keyspace-events", this.keyspaceNotificationsConfigParameter); this.doRegister(this.listenerContainer); }
doRegister 注册监听
public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {
private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*");
private final RedisMessageListenerContainer listenerContainer;
private String keyspaceNotificationsConfigParameter = "EA";
public KeyspaceEventMessageListener(RedisMessageListenerContainer listenerContainer) {
Assert.notNull(listenerContainer, "RedisMessageListenerContainer to run in must not be null!");
this.listenerContainer = listenerContainer;
}
public void onMessage(Message message, @Nullable byte[] pattern) {
if (!ObjectUtils.isEmpty(message.getChannel()) && !ObjectUtils.isEmpty(message.getBody())) {
this.doHandleMessage(message);
}
}
protected abstract void doHandleMessage(Message var1);
public void init() {
if (StringUtils.hasText(this.keyspaceNotificationsConfigParameter)) {
RedisConnection connection = this.listenerContainer.getConnectionFactory().getConnection();
try {
Properties config = connection.getConfig("notify-keyspace-events");
if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
connection.setConfig("notify-keyspace-events", this.keyspaceNotificationsConfigParameter);
}
} finally {
connection.close();
}
}
this.doRegister(this.listenerContainer);
}
protected void doRegister(RedisMessageListenerContainer container) {
this.listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
}
public void destroy() throws Exception {
this.listenerContainer.removeMessageListener(this);
}
public void setKeyspaceNotificationsConfigParameter(String keyspaceNotificationsConfigParameter) {
this.keyspaceNotificationsConfigParameter = keyspaceNotificationsConfigParameter;
}
public void afterPropertiesSet() throws Exception {
this.init();
}
}
KeyExpirationEventMessageListener 监听Key失效事件 private static final Topic KEYEVENT_EXPIRED_TOPIC = new PatternTopic("keyevent@*:expired"); 监听所以db下的key失效
public class KeyExpirationEventMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware {
private static final Topic KEYEVENT_EXPIRED_TOPIC = new PatternTopic("__keyevent@*__:expired");
@Nullable
private ApplicationEventPublisher publisher;
public KeyExpirationEventMessageListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
protected void doRegister(RedisMessageListenerContainer listenerContainer) {
listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC);
}
protected void doHandleMessage(Message message) {
this.publishEvent(new RedisKeyExpiredEvent(message.getBody()));
}
protected void publishEvent(RedisKeyExpiredEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(event);
}
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
}
但是他没有实现 key插入修改的监听,所以这里得自己去实现了
代码实现
KeySetEventMessageListener 监听key set事件 private static final Topic KEYEVENT_SET_TOPIC = new PatternTopic("keyevent@*:set") redis 插入和修改 都是set onMessage() 中就是你要实现的代码了这个是全库监听,你也可以单独监听某个db keyevent@0:set
public class KeySetEventMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware {
private static final Topic KEYEVENT_SET_TOPIC = new PatternTopic("__keyevent@*__:set");
@Nullable
private ApplicationEventPublisher publisher;
public KeySetEventMessageListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void doRegister(RedisMessageListenerContainer listenerContainer) {
listenerContainer.addMessageListener(this, KEYEVENT_SET_TOPIC);
}
@Override
public void doHandleMessage(Message message) {
this.publishEvent(new RedisKeyExpiredEvent(message.getBody()));
}
public void publishEvent(RedisKeyExpiredEvent event) {
if (this.publisher != null) {
this.publisher.publishEvent(event);
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
@Override
public void onMessage(Message message, byte[] pattern) {
// super.onMessage(message, pattern);
// TODO 要实现的逻辑代码
System.out.println("接收数据:" + message.toString());
System.out.println("订阅频道:" + new String(message.getChannel()));
}
}
如果为了保持风格一致,也可以在写个子类继承KeySetEventMessageListener doRegister()注册监听
@Component
public class MyEventMessageListener extends KeySetEventMessageListener {
// spring.redis.keyevent=__keyevent@13__:set
@Value("${spring.redis.keyevent}")
private String keyevent;
public MyEventMessageListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void doRegister(RedisMessageListenerContainer listenerContainer) {
listenerContainer.addMessageListener(this, new PatternTopic(keyevent));
}
@Override
public void onMessage(Message message, byte[] pattern) {
String redisKey = message.toString();
//需要正则匹配出需要处理的event
if (StringUtils.isNotBlank(redisKey) && Pattern.matches("正则匹配", redisKey)) {
System.out.println("接收数据:" + message.toString()+"--"+LocalDateTime.now());
// System.out.println("订阅频道:" + new String(message.getChannel()));
super.onMessage(message, pattern);
}
}
}