Redis key过期监听

通过开启key过期的事件通知,当key过期时,会发布过期事件;我们定义key过期事件的监听器,当key过期时,就能收到回调通知。

注意:

  1)由于redis key过期删除是定时+惰性,当key过多时,删除会有延迟,回调通知同样会有延迟。

  2)且通知是一次性的,没有ack机制,若收到通知后处理失败,将不再收到通知。需自行保证收到通知后处理成功。

        3)通知只能拿到key,拿不到value

使用场景:

  1)实现延时队列

    消息作为key,将需要延迟的时间设置为key的TTL,当key过期时,在监听器收到通知,达到延迟的效果。

步骤:  

  1、修改 redis.conf / redis.window.conf 

  开启 notify-keyspace-events Ex

  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.

原文地址:https://www.cnblogs.com/yangyongjie/p/14399707.html