Redis监听新增、修改、删除、过期事件

前言 项目中需要监听redis的一些事件比如键删除,修改,过期等。过期事件网上有很多例子可以参考,但修改或删除事件却很少。因为redis支持发布订阅所以其他的事件类型应该也是能实现的,通过过期事件监听结合上文键空间通知,我整理了相关代码,希望帮助需要的人快速解决问题。
代码实践 网上示例的失效事件监听代码

import com.alibaba.fastjson.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; import java.util.Date; @Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); }/** * 针对redis数据失效事件,进行数据处理 * @param message message.toString()获取失效的key * @param pattern */ @Override public void onMessage(Message message, byte[] pattern) { String expiredKey = message.toString(); // 获得key之后可以做自己的逻辑操作 } } }

可以看到继承了KeyExpirationEventMessageListener ,这是spring框架已经集成实现的。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) //package org.springframework.data.redis.listener; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.core.RedisKeyExpiredEvent; import org.springframework.lang.Nullable; 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; } }

其他的比如删除事件他没有实现,那我就考虑模仿spring也写个这样的Listener。
简单了解redis键空间通知是怎么回事:
参考文档http://redisdoc.com/topic/notification.html
键空间通知,所有通知以 keyspace@ 为前缀
键事件通知,所有通知以 keyevent@ 为前缀
所有命令都只在键真的被改动了之后,才会产生通知,比如删除foo会产生
键空间通知
"pmessage","__ key__:","__ keyspace@0__:foo","set"
和键事件通知
"pmessage","__ key__:","__ keyevent@0__:set","foo"
如果要监听某个key的执行了什么操作,就订阅 keyspace@,监听某种操作动了哪些key,就订阅keyevent@
这里我们需要监听哪些键被删除了,所以要监听删除动作"__ keyevent@0__:del"
对比上文spring写的listener有这么一句
private static final Topic KEYEVENT_EXPIRED_TOPIC = new PatternTopic("__keyevent@*__:expired");
其他都是模式化的代码,只要修改对应的topic类型就能实现监听了。
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.core.RedisKeyExpiredEvent; 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.lang.Nullable; public class KeyDeleteEventMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware { private static final Topic KEYEVENT_DELETE_TOPIC = new PatternTopic("__keyevent@*__:del"); @Nullable private ApplicationEventPublisher publisher; public KeyDeleteEventMessageListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); }protected void doRegister(RedisMessageListenerContainer listenerContainer) { listenerContainer.addMessageListener(this, KEYEVENT_DELETE_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; } }

类似的新增和修改也一样
package cn.fatri.jarvis.device.shadow.listener; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.core.RedisKeyExpiredEvent; 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.lang.Nullable; public class KeyUpdateEventMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware { private static final Topic KEYEVENT_DELETE_TOPIC = new PatternTopic("__keyevent@*__:set"); @Nullable private ApplicationEventPublisher publisher; public KeyUpdateEventMessageListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); }protected void doRegister(RedisMessageListenerContainer listenerContainer) { listenerContainer.addMessageListener(this, KEYEVENT_DELETE_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; } }

【Redis监听新增、修改、删除、过期事件】我们同样继承这些类就能拿到监听的message了
import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.util.stream.Collectors; @Component @Slf4j public class RedisKeyDeleteListener extends KeyDeleteEventMessageListener {public RedisKeyDeleteListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); }/** * 针对redis数据删除事件,进行数据处理 * @param message message.toString()获取失效的key * @param pattern */ @Override public void onMessage(Message message, byte[] pattern) { String deleteKey = message.toString(); // ...... } } }

    推荐阅读