【Redis系列】Spring boot實現監聽Redis key失效事件

抱明月而長終發表於2021-01-25

talk is cheap, show me the code.

一、開啟Redis key過期提醒

  • 方式二:修改配置檔案

    redis.conf

    # 預設 notify-keyspace-events ""
    notify-keyspace-events Ex
    
  • 方式二:命令列開啟

    CONFIG SET notify-keyspace-events Ex
    CONFIG GET notify-keyspace-events
    

二、notify-keyspace-events

notify-keyspace-events 選項的預設值為空

notify-keyspace-events 的引數可以是以下字元的任意組合, 它指定了伺服器該傳送哪些型別的通知。

字元 傳送的通知
K 鍵空間通知,所有通知以 keyspace@ 為字首
E 鍵事件通知,所有通知以 keyevent@ 為字首
g DEL 、 EXPIRE 、 RENAME 等型別無關的通用命令的通知
$ 字串命令的通知
l 列表命令的通知
s 集合命令的通知
h 雜湊命令的通知
z 有序集合命令的通知
x 過期事件:每當有過期鍵被刪除時傳送
e 驅逐(evict)事件:每當有鍵因為 maxmemory 政策而被刪除時傳送
A 引數 g$lshzxe 的別名

三、Coding

  1. 初始化一個Spring Boot專案

  2. pom.xml

    <dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-data-redis</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.projectlombok</groupId>
    			<artifactId>lombok</artifactId>
    		</dependency>
    	</dependencies>
    
  3. 定義配置類RedisListenerConfig

    @Configuration
    public class RedisListenerConfig {
    
    	@Bean
    	RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    		RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    		container.setConnectionFactory(connectionFactory);
    		return container;
    	}
    
    }
    
  4. 定義資料生產類ProviderDataToRedis

    @Slf4j
    @Component
    public class ProviderDataToRedis implements CommandLineRunner {
    
    	@Autowired
    	private StringRedisTemplate stringRedisTemplate;
    
    	@Override
    	public void run(String... args) throws Exception {
    		int[] num = new int[]{1};
    		Random random = new Random();
    		while (true) {
    			int max = random.nextInt(5);
    			IntStream.range(0, max).forEach(n -> stringRedisTemplate.opsForValue().set(String.format("mq:s1:%s", ++num[0]), "已預訂", 5, TimeUnit.SECONDS));
    			log.info("放了 {} 條資料到redis...", max);
    			TimeUnit.SECONDS.sleep(3);
    		}
    	}
    }
    
  5. 定義監聽器 實現KeyExpirationEventMessageListener介面

    檢視原始碼發現,該介面監聽所有db的過期事件keyevent@*:expired"

    定義Status1ExpirationListener監聽狀態1到期

    @Slf4j
    @Component
    public class Status1ExpirationListener extends KeyExpirationEventMessageListener {
    
    	public Status1ExpirationListener(RedisMessageListenerContainer listenerContainer) {
    		super(listenerContainer);
    	}
    
    	@Autowired
    	private StringRedisTemplate stringRedisTemplate;
    
    	@Override
    	public void onMessage(Message message, byte[] pattern) {
    		// 使用者做自己的業務處理即可,注意message.toString()可以獲取失效的key
    		String expiredKey = message.toString();
    		if (expiredKey.startsWith("mq:s1:")) {
    			log.info("-----------------------------------");
    			log.info(String.format("過期key[%s]", expiredKey));
    			String newKey = String.format("mq:s2:%s", expiredKey.substring(6));
    			String newValue = "行程中";
    			stringRedisTemplate.opsForValue().set(newKey, newValue, 3, TimeUnit.SECONDS);
    			log.info(String.format("%s: %s", newKey, newValue));
    			log.info("-----------------------------------");
    		}
    	}
    
    }
    

    定義Status2ExpirationListener監聽狀態2到期

    @Slf4j
    @Component
    public class Status2ExpirationListener extends KeyExpirationEventMessageListener {
    
    	public Status2ExpirationListener(RedisMessageListenerContainer listenerContainer) {
    		super(listenerContainer);
    	}
    
    	@Override
    	public void onMessage(Message message, byte[] pattern) {
    		// 使用者做自己的業務處理即可,注意message.toString()可以獲取失效的key
    		String expiredKey = message.toString();
    		if (expiredKey.startsWith("mq:s2:")) {
    			log.info("***********************************");
    			log.info(String.format("過期key[%s]", expiredKey));
    			log.info("[{}]行程已完成,修改資料庫狀態。", newKey);
    			log.info("***********************************");
    		}
    	}
    
    }
    

四、測試輸出

...
2021-01-25 23:16:58.012  INFO 55511 --- [           main] n.y.tools.listener.ProviderDataToRedis      : 放了 4 條資料到redis...
2021-01-25 23:17:00.037  INFO 55511 --- [ container-1070] c.i.r.l.Status1ExpirationListener        : -----------------------------------
2021-01-25 23:17:00.037  INFO 55511 --- [ container-1070] c.i.r.l.Status1ExpirationListener        : 過期key[mq:s1:272]
2021-01-25 23:17:00.037  INFO 55511 --- [ container-1072] c.i.r.l.Status1ExpirationListener        : -----------------------------------
2021-01-25 23:17:00.037  INFO 55511 --- [ container-1072] c.i.r.l.Status1ExpirationListener        : 過期key[mq:s1:271]
2021-01-25 23:17:00.039  INFO 55511 --- [ container-1070] c.i.r.l.Status1ExpirationListener        : mq:s2:272: 行程中
2021-01-25 23:17:00.039  INFO 55511 --- [ container-1072] c.i.r.l.Status1ExpirationListener        : mq:s2:271: 行程中
2021-01-25 23:17:00.039  INFO 55511 --- [ container-1070] c.i.r.l.Status1ExpirationListener        : -----------------------------------
2021-01-25 23:17:00.039  INFO 55511 --- [ container-1072] c.i.r.l.Status1ExpirationListener        : -----------------------------------
2021-01-25 23:17:00.140  INFO 55511 --- [ container-1075] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.140  INFO 55511 --- [ container-1075] c.i.r.l.Status2ExpirationListener        : 過期key[mq:s2:270]
2021-01-25 23:17:00.140  INFO 55511 --- [ container-1075] c.i.r.l.Status2ExpirationListener        : [270]行程已完成,修改資料庫狀態。
2021-01-25 23:17:00.140  INFO 55511 --- [ container-1075] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1077] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1077] c.i.r.l.Status2ExpirationListener        : 過期key[mq:s2:269]
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1079] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1079] c.i.r.l.Status2ExpirationListener        : 過期key[mq:s2:268]
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1077] c.i.r.l.Status2ExpirationListener        : [269]行程已完成,修改資料庫狀態。
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1077] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1079] c.i.r.l.Status2ExpirationListener        : [268]行程已完成,修改資料庫狀態。
2021-01-25 23:17:00.242  INFO 55511 --- [ container-1079] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.546  INFO 55511 --- [ container-1081] c.i.r.l.Status2ExpirationListener        : ***********************************
2021-01-25 23:17:00.546  INFO 55511 --- [ container-1081] c.i.r.l.Status2ExpirationListener        : 過期key[mq:s2:267]
2021-01-25 23:17:00.546  INFO 55511 --- [ container-1081] c.i.r.l.Status2ExpirationListener        : [267]行程已完成,修改資料庫狀態。
2021-01-25 23:17:00.546  INFO 55511 --- [ container-1081] c.i.r.l.Status2ExpirationListener        : ***********************************
...

五、一直增加的執行緒數

從測試輸出的日誌中可以看出,執行緒一直在增加,這個問題還有待解決!

六、參考

http://redisdoc.com/topic/notification.html

相關文章