redis過期監聽
我的應用場景:因為業務需求,我們會每10分鐘從kafka得到資料開始處理,這時就會存在一種情況,如果kafka資料沒有傳遞過來,我們是不是要通過一種方式知道資料沒傳遞通知我們。在這裡我選用的模式是redis提供的觀察者模式。
原理:從kafka得到的資料儲存到redis中,key的失效時間可以自定義配置(我定義15分鐘失效),每次從kafka得到資料都去重新整理redis,這樣如果kafka每次都傳遞資料,redis就不會失效,如果不傳遞資料,redis就會失效,然後通過redis的監聽器得到這個失效的redis再進行後續處理(我們這邊是進行郵件報警)
1:配置redis的失效監聽,需要修改redis.conf配置檔案
增加:notify-keyspace-events "Ex"
配置檔案中找到notify-keyspace-events,修改成notify-keyspace-events "Ex"
Ex 的解釋如下:
2:配置檔案修改好後,重新啟動redis,我的redis是用docker啟動的。重新啟動了容器。
3:驗證redis失效監聽是否好用。
進入redis容器:
docker exec -it redis /bin/sh
執行redis客戶端:
redis-cli
執行監聽命令:
psubscribe __keyevent@0__:expired
再啟動一個redis-cli
建立一個10秒後失效的reids:
setex test 10 test
10秒後,可以看到監聽埠可以接收到失效的redis的key.
4:java程式碼編寫,pom.xml引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
5:配置redis的config,配置了兩種方式,第一種方式是支援@Cacheable建立redis.第二種方式是直接引用redis提供的StringRedisTemplate類來呼叫redis的set和get方法。
第一種方式的配置檔案寫法:RedisCacheConfig.java
@Configuration
public class RedisCacheConfig{
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory),
this.getRedisCacheConfigurationBase(), // 預設策略
this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put("NoDataCache0", this.getRedisCacheConfigurationWithTtl(60));
return redisCacheConfigurationMap;
}
private RedisCacheConfiguration getRedisCacheConfigurationBase() {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
//.entryTtl(this.timeToLive) --定義到期時間
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
//.disableCachingNullValues(); //儲存的資料不能為null
return config;
}
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(seconds)) //定義到期時間
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
//.disableCachingNullValues(); //儲存的資料不能為null
return config;
}
}
用RedisCacheManager的構造方法來實現,我定義了一個60秒失效的策略,redis的寫法如下:
@Cacheable(value = "NoDataCache0" key="'rc_alarmrule_2_'+#tenantId+'_'+#metricSpecId")
public List<AlarmRuleRedisAllVO> getAlarmRuleAllRedis(long tenantId,long metricSpecId) {
return null;
}
1)value = "NoDataCache0" 是我策略裡定義的名稱redisCacheConfigurationMap.put("NoDataCache0", this.getRedisCacheConfigurationWithTtl(60));
這個可以定義多個。每個的名稱和失效時間配置不一樣。
2)這種方式的配置,失效時間只能寫在配置檔案中或者寫死,如果我想按照我表中定義一個失效時間實時的進行變化就做不到了。所以我又用了第二種配置方案。
第二種方式的配置檔案寫法:RedisCacheConfig.java
@Service
public class RedisService {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 永久
* @param key
* @param value
*/
public void set(String key, Object value) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value));
}
/**
* 將 key,value 存放到redis資料庫中,設定過期時間單位是秒
* @param key
* @param value
* @param timeout
*/
public void setBySeconds(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), timeout, TimeUnit.SECONDS);
}
/**
* 將 key,value 存放到redis資料庫中,設定過期時間單位是分鐘
* @param key
* @param value
*/
public void setByMinutes(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), timeout, TimeUnit.MINUTES);
}
/**
* 將 key,value 存放到redis資料庫中,設定過期時間單位是小時
* @param key
* @param value
*/
public void setByHours(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), timeout, TimeUnit.HOURS);
}
/**
* 將 key,value 存放到redis資料庫中,設定過期時間單位是天
* @param key
* @param value
*/
public void setByDays(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), timeout, TimeUnit.DAYS);
}
/**
* 刪除 key 對應的 value
* @param key
*/
public void delete(String key) {
redisTemplate.delete(key);
}
/**
* 獲取與 key 對應的物件
* @param key
* @param clazz 目標物件型別
* @param <T>
* @return
*/
public <T> T get(String key, Class<T> clazz) {
String s = get(key);
if (s == null) {
return null;
}
return JsonUtil.convertString2Obj(s, clazz);
}
/**
* 獲取與 key 對應的物件
* @param key
* @param clazz 目標物件型別
* @param <T>
* @return
* @throws IOException
* @throws JsonMappingException
* @throws JsonParseException
*/
public <T> List<T> getList(String key, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
String s = get(key);
if (s == null) {
return null;
}
return JsonUtil.toList(s,clazz);
}
/**
* 獲取 key 對應的字串
* @param key
* @return
*/
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 查詢 key 對應的過期時間
* @param key
* @return
*/
public String getExpire(String key){
Long timeout = redisTemplate.getExpire(key,TimeUnit.MILLISECONDS);
System.out.println(timeout);
if (timeout < 0)
return "Has expired!";
Long milliSecond = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli() + timeout;
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(LocalDateTime.ofInstant(
Instant.ofEpochMilli(milliSecond),ZoneId.systemDefault()));
}
/**
* 判斷 key 是否在 redis 資料庫中
* @param key
* @return
*/
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
這種方式可以實時的改變redis的失效時間。
6:配置redis失效的監聽config RedisListenerConfig.java
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// container.addMessageListener(new RedisExpiredListener(), new PatternTopic("__keyevent@0__:expired"));
return container;
}
}
我沒有配置__keyevent@0__:expired",對某個db進行監聽,RedisMessageListenerContainer有個預設的配置是對所有的db進行監聽。
7:redis的監聽類 RedisKeyExpirationListener.java
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
/**
* 針對redis資料失效事件,進行資料處理
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
//message.toString()可以獲取失效的key
String expiredKey = message.toString();
logger.debug("失效的redis是:"+expiredKey);
String part = "NoDataCache0";
//如果是NoDataCache0:開頭的key,進行處理
if(expiredKey.startsWith(part)){
//自己的業務邏輯
}
}
1)注意:失效的redis只能得到key,是得不到value的,所以業務邏輯如果用到value裡的值需要把值寫到key中。
相關文章
- java 監聽 redis 過期事件JavaRedis事件
- Laravel 如何監聽 Redis key 過期進行回撥LaravelRedis
- predis 監聽不到 key 過期事件,Redis-cli 可以收到Redis事件
- Redis中監聽key過期通知Redis
- 永遠不要使用 redis 過期監聽實現定時任務Redis
- 領導:誰再用redis過期監聽實現關閉訂單,立馬滾蛋!Redis
- Laravel Telescope 未監聽 RedisLaravelRedis
- Flutter - 生命週期監聽和管理Flutter
- Redis威脅流量監聽實踐Redis
- redis 過期時間Redis
- Android 監聽生命週期工具庫Android
- 監聽器和過濾器過濾器
- Redis過期刪除策略Redis
- 【Redis系列】Spring boot實現監聽Redis key失效事件RedisSpring Boot事件
- 透過觀察者監聽模型事件模型事件
- 通過觀察者監聽模型事件模型事件
- redis hset hmset過期時間Redis
- redis設定過期時間Redis
- redis 過期鍵刪除策略Redis
- 過濾器和監聽器總結過濾器
- 監聽 watch props物件屬性監聽 或深度監聽物件
- Redis過期策略及實現原理-Redis面試題Redis面試題
- Redis 的持久化與過期鍵Redis持久化
- 動態監聽與靜態監聽
- 動態監聽和靜態監聽
- 【JavaWeb】EL表示式&過濾器&監聽器JavaWeb過濾器
- 【監聽】兩庫互配靜態監聽
- ORACLE動態監聽與靜態監聽Oracle
- oracle靜態監聽和動態監聽Oracle
- 【oracle】動態監聽與靜態監聽Oracle
- 走近原始碼:Redis如何清除過期key原始碼Redis
- Redis 修改過期 key 的一個坑Redis
- Redis鍵不會自動過期 - AblyRedis
- Redis sortedset實現元素自動過期Redis
- Redis 中如何讓訊息永不過期Redis
- Redis(二十):Redis資料過期和淘汰策略詳解(轉)Redis
- 事件監聽事件
- Oracle 監聽Oracle