SpringBoot Redis 主键监听

1,361 阅读2分钟

基础知识

`redis自2.8.0之后版本提供Keyspace Notifications功能,允许客户订阅Pub / Sub频道,以便以某种方式接收影响Redis数据集的事件

链接地址:redis.io/topics/noti…

默认情况下,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);
        }
    }

}