Redis key过期监听
通过开启key过期的事件通知,当key过期时,会发布过期事件;我们定义key过期事件的监听器,当key过期时,就能收到回调通知。
注意:
1)由于Redis key过期删除是定时+惰性,当key过多时,删除会有延迟,回调通知同样会有延迟。因此性能较低
2)且通知是一次性的,没有ack机制,若收到通知后处理失败,将不再收到通知。需自行保证收到通知后处理成功。
3)通知只能拿到key,拿不到value
4)Redis将数据存储在内存中,如果遇到恶意下单或者刷单的将会给内存带来巨大压力
使用场景:
1)实现延时队列
消息作为key,将需要延迟的时间设置为key的TTL,当key过期时,在监听器收到通知,达到延迟的效果。
步骤:
1、修改 redis.conf / redis.window.conf
开启 notify-keyspace-events Ex
补充,notify-keyspace-events 配置选项:
K Keyspace events, published with __keyspace@<db>__ prefix.(键空间通知)
E Keyevent events, published with __keyevent@<db>__ prefix.(键事件通知)
g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...(通用命令(非类型特定的),如DEL、EXPIRE、RENAME)
$ String commands(字符串命令)
l List commands(列表命令)
s Set commands(集合命令)
h Hash commands(哈希命令)
z Sorted set commands(有序集合命令)
x Expired events (events generated every time a key expires)(过期事件(每次密钥过期时生成的事件))
e Evicted events (events generated when a key is evicted for maxmemory)(驱逐事件(当为maxmemory退出一个键时生成的事件))
t Stream commands(Stream命令)
d Module key type events(模块key类型事件)
m Key-miss events (Note: It is not included in the 'A' class)(Key-miss事件(当访问不存在的键时通知,不包含在A中))
A Alias for g$lshzxetd(g$lshzxetd的别名都可用A表示), so that the "AKE" string means all the events(Except key-miss events which are excluded from 'A' due to their unique nature)(用“AKE”可表示所有事件通知,除了特殊的Key-miss事件)
获取配置:CONFIG GET notify-keyspace-events
2、RedisConfig 中配置 Message Listener Containers (消息订阅者容器)
类似于Redis pub/sub 中 Message Listener Containers 的配置,区别少了监听器的指定。
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
// redis 消息订阅(监听)者容器
RedisMessageListenerContainer messageListenerContainer = new RedisMessageListenerContainer();
messageListenerContainer.setConnectionFactory(redisConnectionFactory);
// messageListenerContainer.addMessageListener(new ProductUpdateListener(), new PatternTopic("*.product.update"));
return messageListenerContainer;
}
3、定义 key过期监听器,继承 KeyExpirationEventMessageListener
@Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { /** * 创建RedisKeyExpirationListener bean时注入 redisMessageListenerContainer * * @param redisMessageListenerContainer RedisConfig中配置的消息监听者容器bean */ public RedisKeyExpirationListener(RedisMessageListenerContainer redisMessageListenerContainer) { super(redisMessageListenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { String channel = new String(message.getChannel()); // __keyevent@*__:expired String pa = new String(pattern); // __keyevent@*__:expired String expiredKey = message.toString(); System.out.println("监听到过期key:" + expiredKey); } }
可以看到,其本质是Redis的发布订阅,当key过期,发布过期消息(key)到Channel :__keyevent@*__:expired中,再看KeyExpirationEventMessageListener 源码:
public class KeyExpirationEventMessageListener extends KeyspaceEventMessageListener implements ApplicationEventPublisherAware { // 发布key过期频道的topic private static final Topic KEYEVENT_EXPIRED_TOPIC = new PatternTopic("__keyevent@*__:expired"); private @Nullable ApplicationEventPublisher publisher; /** * 子类在由Spring容器创建bean的时候调用父类的此构造器,并传入容器中的RedisMessageListenerContainer bean作为参数 */ public KeyExpirationEventMessageListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } /** * doRegister 在 KeyspaceEventMessageListener(实现了InitializingBean和 MessageListener) 中的init方中被调用 * 将我们自定义的key过期监听器添加到 消息监听容器中 */ @Override protected void doRegister(RedisMessageListenerContainer listenerContainer) { listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC); } /** * 发布key过期事件 * * @param message 过期key */ @Override protected void doHandleMessage(Message message) { publishEvent(new RedisKeyExpiredEvent(message.getBody())); } /** * 由Spring RedisKeyExpiredEvent事件监听器执行实际上的redis key过期消息的发布 */ protected void publishEvent(RedisKeyExpiredEvent event) { if (publisher != null) { this.publisher.publishEvent(event); } } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.publisher = applicationEventPublisher; } }
因此,我们定义的继承了KeyExpirationEventMessageListener的redis key 过期监听器本质上就是一个消息监听器,监听来自channel为__keyevent@*__:expired 上发布的消息
END.