redis过期事件监听、可以做延时任务
在使用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提供依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
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;@Autowiredpublic RedisCallBackConfig(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}/* 处理数据过期的数据 @time 2021/12/27 14:32*/@Overridepublic 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 {@Resourceprivate 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 {@Resourceprivate OrderUtils orderUtils;@Resourceprivate UsersPayRecordService usersPayRecordService;@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate 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博客