监听 Redis 缓存过期(Key 失效)事件

本贴最后更新于 725 天前,其中的信息可能已经渤澥桑田

监听 Redis 缓存过期(Key 失效)事件

最近写公司一个项目遇到一个场景,设备上线后会以 0.5HZ 的频率给后台发送状态消息,20 秒内没有重连则认为设备下线,需要执行相应操作。

首先想到的就是在 Redis 存带过期时间的 key,每次设备发来消息就去刷新 key 的过期时间,key 过期后,则执行设备下线的方法,那么问题来了,这怎么去监听 key 是否过期(当时没听说过 redis 还可以发布/订阅),于是头脑风暴了一下,直接看 Redis 监听的可以跳过。

头脑风暴一:

每次设备上线都在在Redis里存储: 1. <id_time,value>过期时间20秒 2. <id_backup,value>不包含过期时间 开启一个定时任务:每隔30秒去Redis获取一下这两组数据 每组数据都包含多个,因为可能有多个设备在线 对比这两组数据,即可找到过期的数据,也就是下线的设备(id_backup有,id_time已经失效的数据) 然后去执行相应的操作。

但是不太想整个 Redis
头脑风暴二

干脆直接在Java中维护一个Map<id,最后状态时间戳>(当然我做的这个项目在线设备不多,百八十个不得了了) 每次设备发送状态信息,刷新一下map里的`最后状态时间戳` 然后开启一个定时任务,每隔30秒去遍历一下map 哪个数据的'最后状态时间戳'比当前时间早了超过30秒则认为设备下线,指向相应操作 这种方法虽然不用整合Redis但是容易导致数据丢失,一旦服务停止,内存中数据不会持久化。

于是网上找了一下相关 Redis 监听相关方法,还真有,下面我把 SpringBoot 整合 Redis,实现 Redis 缓存过期(Key 失效)事件监听相关代码分享

Redis 缓存过期(Key 失效)事件监听

Redis 配置修改

  1. 事件通过 Redis 的订阅与发布功能(pub/sub)来进行分发, 故需要开启 redis 的事件监听与发布
  2. 修改 redis.conf 文件(Windows 上是 redis.windows.conf 和 redis.windows-service.conf)
notify-keyspace-events Ex
  1. 重启 Redis,然后进行下面测试。
  2. 打开 redis-cli.exe 窗口输入:PSUBSCRIBE __keyevent@*__:expired,窗口先别关闭。
    在这里插入图片描述
  3. 再打开一个 redis-cli.exe 窗口,存一个带失效时间的数据 setex boy 6 6
    在这里插入图片描述
    查看第一个窗口,监听到数据失效,即配置成功
    在这里插入图片描述
  4. 引入 pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
  1. application.yaml 配置文件
spring: redis: host: localhost port: 6379 password: jedis: pool: max-active: 0
  1. RedisListenerConfig.java
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisListenerConfig { @Autowired private RedisTemplate redisTemplate; @Bean RedisMessageListenerContainer listenerContainer(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer listenerContainer = new RedisMessageListenerContainer(); listenerContainer.setConnectionFactory(connectionFactory); return listenerContainer; } @Bean KeyExpirationEventMessageListener redisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { return new RedisKeyExpirationListener(listenerContainer); } @Bean public RedisTemplate redisTemplateInit() { //设置序列化Key的实例化对象 redisTemplate.setKeySerializer(new StringRedisSerializer()); //设置序列化Value的实例化对象 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } }
  1. RedisKeyExpirationListener.java
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.listener.KeyExpirationEventMessageListener; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.stereotype.Service; @Slf4j @Service public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { //获取过期的key String expireKey = message.toString(); //信息打印 log.info("key : {} 失效" ,expireKey); //设备下线相关操作 } }
  1. IRedisService.java
public interface IRedisService { /** * 存入k,v键值对,有默认生效时间 * * @param key * @param value */ void setValue(String key, String value); }
  1. RedisServiceImpl.java
import com.tangyh.lamp.utils.Constant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.TimeUnit; @Service public class RedisServiceImpl implements IRedisService { @Autowired private RedisTemplate redisTemplate; @Override public void setValue(String key, String value) { ValueOperations<String, Object> vo = redisTemplate.opsForValue(); vo.set(key, value); redisTemplate.expire(key, Constant.OSD_TIME_OUT, TimeUnit.SECONDS); // 这里指的是30秒后失效 } }

有错误之处,或者更好的办法还请评论区指教。

  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8216 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...