一、前言
Redis提供了数据变化的通知事件,可以实时监测key和value的变化,客户端可以通过订阅相关的channel来接收这些通知事件,然后做相应的自定义处理,详细的介绍可以参考官方文档Redis keyspace notifications | Docs
使用Redis时,可以在配置文件中管理相应的配置,默认配置是不开启事件通知的。
############################# Event notification ##############################
# Redis can notify Pub/Sub clients about events happening in the key space.
# This feature is documented at http://redis.io/topics/notifications
#
# For instance if keyspace events notification is enabled, and a client
# performs a DEL operation on key "foo" stored in the Database 0, two
# messages will be published via Pub/Sub:
#
# PUBLISH __keyspace@0__:foo del
# PUBLISH __keyevent@0__:del foo
#
# It is possible to select the events that Redis will notify among a set
# of classes. Every class is identified by a single character:
#
# 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, ...
# $ 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)
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
#
# The "notify-keyspace-events" takes as argument a string that is composed
# of zero or multiple characters. The empty string means that notifications
# are disabled.
#
# Example: to enable list and generic events, from the point of view of the
# event name, use:
#
# notify-keyspace-events Elg
#
# Example 2: to get the stream of the expired keys subscribing to channel
# name __keyevent@0__:expired use:
#
# notify-keyspace-events Ex
#
# By default all notifications are disabled because most users don't need
# this feature and the feature has some overhead. Note that if you don't
# specify at least one of K or E, no events will be delivered.
notify-keyspace-events ""
二、整合方案
1、自定义监听事件
该方案是自定义监听器messageListener,监听指定的频道"__keyevent@*",表示监听所有事件,这种方案比较灵活,可以自定义监听的事件频道。但是,这种需要redis服务配置开启通知,例如配置notify-keyspace-events为AKE,表示开启全部事件通知。如果redis配置不正确,是不会通知指定事件的。另外,如果修改了该配置还需要重启Redis服务,所以慎重选择该方式。
redis配置如下:
# redis配置开启全部事件通知
notify-keyspace-events AKE
代码部分如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@Configuration
public class RedisListenerConfig {
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 订阅一个全部变化事件频道 __keyevent@*
container.addMessageListener(listenerAdapter, new PatternTopic("__keyevent@*"));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter() {
// 如果使用自定义方法myHandleMessage,就把方法名作为第二个参数添入
// return new MessageListenerAdapter(new RedisKeyChangeSubscriber(), "myHandleMessage");
// 如果不传入第二个参数,则会使用默认方法handleMessage
return new MessageListenerAdapter(new RedisKeyChangeSubscriber());
}
public static class RedisKeyChangeSubscriber {
// 自定义处理方法
public void myHandleMessage(String message) {
System.out.println("RedisKeyChangeSubscriber Received message: " + message);
// 在这里处理收到的消息
}
// message对应redis数据的key
public void handleMessage(String message) {
System.out.println("RedisKeyChangeSubscriber Received message: " + message);
// 在这里处理收到的消息
}
}
}
测试效果:
①创建键值对{"key1": "val1"}
2、继承spring boot集成的监听器KeyspaceEventMessageListener
spring官方提供的redis starter依赖包含了事件变化监听器KeyspaceEventMessageListener,是个抽象类,可以通过继承这个抽象类重写doHandleMessage方法来自定义数据变化的处理逻辑。这种方式不需要关注Redis配置,简化了监听事件变化的处理,容易上手。没有特别要求的话,也推荐使用这种方式。同样的,该依赖也提供了redis数据过期事件的监听器KeyExpirationEventMessageListener,用于监听redis数据过期的事件,使用方法和KeyspaceEventMessageListener大体一致,但是KeyExpirationEventMessageListener不是抽象类。
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.KeyspaceEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@Configuration
public class RedisListenerConfig {
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
@Bean
RedisKeyChangerHandler redisKeyChangerHandler(RedisMessageListenerContainer container) {
return new RedisKeyChangerHandler(container);
}
public static class RedisKeyChangerHandler extends KeyspaceEventMessageListener {
/**
* Creates new {@link KeyspaceEventMessageListener}.
*
* @param listenerContainer must not be {@literal null}.
*/
public RedisKeyChangerHandler(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
// message对应redis数据的key
protected void doHandleMessage(@NotNull Message message) {
System.out.println("KeyspaceEventMessageListener Received message: " + message);
}
}
}
测试效果同方案一。