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”
redis过期事件监听、可以做延时任务_第1张图片

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博客

你可能感兴趣的:(RibbitMq,java,springboot,redis,缓存)