在使用redis时,所有的key都要设置过期时间,过期之后,redis就会把对应的key清除掉。
此方法可以监听redis的key失效,在失效时做一些逻辑处理
redis过期监听 不像mq有保证 不推荐用来弄需要有保证的业务
现象:
redis 过期事件监听
通过KeyExpirationEventMessageListener类实现redis失效监听事件
方法
1.打开redis 监听事件开关
需要修改redis.conf配置文件,找到 EVENT NOTIFICATION (事件通知)这个配置
将 notify-keyspace-events “” 修改为 notify-keyspace-events “Ex”
2. pom.xml提供依赖
org.springframework.boot
spring-boot-starter-data-redis
3.编写自己的接口 提供给业务实现
public interface WxRedisMessageCallBack {
}
4.在redis基础服务定义自定义注解 来控制业务方法监听的redis key
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WxRedisListener {
/** 过期事件订阅的key */
String key();
}
5.redis基础服务继承 KeyExpirationEventMessageListener类 注入自己的接口 获取实现类方法 通过自定义注解中的key与redis过期key匹配来控制每个key过期调用不同方法
package com.hxnwm.ny.diancan.common.config;
import com.hxnwm.ny.diancan.common.annotaiton.WxRedisListener;
import com.hxnwm.ny.diancan.common.callback.WxRedisMessageCallBack;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
@Slf4j
@Configuration
public class RedisCallBackConfig extends KeyExpirationEventMessageListener {
@Autowired(required = false)
private WxRedisMessageCallBack wxRedisMessageLister;
@Autowired
public RedisCallBackConfig(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
/**
* 处理数据过期的数据
*
* @time 2021/12/27 14:32
*/
@Override
public void onMessage(Message message, byte[] pattern) {
String key = new String(message.getBody(), StandardCharsets.UTF_8);
try {
// 获取自己实现类获取实现类全部方法
Method[] methods = wxRedisMessageLister.getClass().getDeclaredMethods();
for (Method method : methods) {
// 获取方法的注解判断是否与过期key一致
WxRedisListener annotation = AnnotationUtils.findAnnotation(method,WxRedisListener.class);
if (annotation == null) {
continue;
}
if (key.contains(annotation.key())) {
method.invoke(wxRedisMessageLister, key);
log.info("[redis消息过期监听] 消费成功:key [{}],message:[{}]", key, message);
}
}
} catch (Exception e) {
log.error("[redis消息过期监听] 消费失败:key [{}],message:[{}]", key, message,e);
}
super.onMessage(message, pattern);
}
}
6.在业务服务实现自己的接口 来实现逻辑。自定义注解来控制监听的key
其中RedisKeyConstant类包含
public static final String ORDER_COIN = "user:order:coin:";
package com.hxnwm.ny.diancan.common.callback;
/**
* @ClassName OrderRedisCallBack
* @Date 2022/10/28 10:36
* @Version 1.0
**/
@Slf4j
@Configuration
public class RedisKeyTimeCallBack implements WxRedisMessageCallBack {
@Resource
private UsersPayRecordService usersPayRecordService;
/**
* 订单贝收益
*
* @param key
*/
@WxRedisListener(key = RedisKeyConstant.ORDER_COIN)
public void coinExpire(String key) {
// 判断是否有支付记录
UsersPayRecord usersPayRecord = this.usersPayRecordService.selectByPayWayInfo(new UsersPayRecord(Integer.parseInt(key.split(":")[3]), Order.PAY_WAY.Wechat.payWay));
// 非空校驗
if(Objects.isNull(usersPayRecord)){
log.info("没有找到key为{}对应支付记录,退出!",key);
return;
}
log.info("监听到用户");
}
}
7.发送测试消息
package com.hxnwm.ny.diancan.controller;
import com.hxnwm.ny.diancan.common.constant.RedisKeyConstant;
import com.hxnwm.ny.diancan.common.utils.OrderUtils;
import com.hxnwm.ny.diancan.service.system.SystemHotSearchService;
import com.hxnwm.ny.diancan.service.users.UsersPayRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
/**
* @ClassName TestController
* @Description TODO
* @Author wdj
* @Date 2023/3/22 16:47
* @Version
*/
@Slf4j
@RestController
@RequestMapping(value = "test")
public class TestController {
@Resource
private OrderUtils orderUtils;
@Resource
private UsersPayRecordService usersPayRecordService;
@Resource
private RedisTemplate redisTemplate;
@Resource
private OrderService orderService;
@RequestMapping(value = "/bb", method = POST)
private void bb(){
Order order = this.orderService.info(3918,"DC1680074408256485");
UsersPayRecord usersPayRecord = this.usersPayRecordService.selectByPayWayInfo(new UsersPayRecord(order.getOrderSn(), Order.PAY_WAY.Wechat.payWay));
if(Objects.nonNull(usersPayRecord)){
//进行收益统计
redisTemplate.opsForValue().set(RedisKeyConstant.ORDER_COIN + usersPayRecord.getId(), usersPayRecord.getId(), 1, TimeUnit.MINUTES);
}
}
}
本文章实现参考了:redis过期事件监听_redis过期监听_淡乄然的博客-CSDN博客