SpringBoot实现Redis失效监听事件——MessageLisenter_oeSeven的专栏-CSDN博客

SpringBoot搭建和集成Redis这里就不说了,如果不会可以自行百度。

步骤:

  1. 首先加入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 创建一个监听类实现MessageListener接口,重写onMessage方法。
    在key失效之后,会出发onMessage方法。
    在这里,我只是获取redis中失效的key值。
    注意:只能获取失效的key值,不能获取key对应的value值。代码中的输出语句为null,可以证明。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

@Component
public class KeyExpiredListentForMessageListener implements MessageListener {

    @Autowired
    public RedisTemplate<String,String> redisTemplate;

    @Override
    public void onMessage(Message message, byte[] bytes) {
        String key = new String(message.getBody(), StandardCharsets.UTF_8);
        String myKey = redisTemplate.opsForValue().get("myKey");
        System.out.println(myKey);
    }
}
  1. 在可以正常连接Redis存取数据之后,创建一个配置类 RedisConfigForMessageListener 代码如下,
    这里的" __keyevent@*__:expired " 中@后面的 * 表示Redis的所有库,也可以指定某一个库进行监听,例如: keyevent@0:expired 表示监听redis中db0的库。
    在这里插入图片描述
import com.qq.redis.KeyExpiredListentForMessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

@Configuration
public class RedisConfigForMessageListener {
    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private RedisConnectionFactory connectionFactory;

    @Bean
    public KeyExpiredListentForMessageListener registerListener() {
        RedisMessageListenerContainer redisMessageListenerContainer = applicationContext.
                getBean(RedisMessageListenerContainer.class);
        redisMessageListenerContainer.setConnectionFactory(connectionFactory);
        KeyExpiredListentForMessageListener listener = new KeyExpiredListentForMessageListener();
        redisMessageListenerContainer.addMessageListener(listener,new PatternTopic("__keyevent@*__:expired"));
        return listener;
    }
}

这样就搞定了,下面写一个Controller测试一下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
public class TestController {

    @Autowired
    public RedisTemplate<String,String> redisTemplate;
    
    @RequestMapping(value = "/redisTest", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public Map<String,Object> redisTest(){
        redisTemplate.opsForValue().set("myKey", "myValue",100, TimeUnit.SECONDS);
        String myKey = redisTemplate.opsForValue().get("myKey");
        System.out.println(myKey);
        Map<String,Object> resultMap = new HashMap<String,Object>();
        resultMap.put("myKey",myKey);
        return resultMap;
    }
}

总结:
客户端监听订阅的topic,当有消息的时候,会触发onMessage方法时,并不能得到value, 只能得到key。

解决方案1: 
    存数据的时候存入2个key,一个有对应的值,一个没有对应的值。
    举个栗子:存入数据 {“myKey”:"123456"} 对应再存入一个{“copy_myKey”:“”}。
    真正的key是"myKey"(业务中使用), 失效触发key是"copy_myKey"(其value为空字符为了减少内存空间消耗)。
    当"copy_myKey"触发失效时, 从"myKey"得到失效时的值, 并在逻辑处理完后"del myKey"
    但此种方式有缺陷:
      1: 存在多余的key; (myKey和copy_myKey)
      2: 不严谨, 假设copykey在 12:00:00失效, 通知在12:10:00收到, 这间隔的10min内程序修改了key, 得到的并不是 失效时的value.
      第1点影响不大; 第2点貌似redis本身的Pub/Sub就不是严谨的, 失效后还存在value的修改, 应该在设计/逻辑上杜绝
      当"copykey:vkey"触发失效时, 从"vkey"得到失效时的值, 并在逻辑处理完后"del vkey"
解决方案2:(推荐) 
     *  使用spring + quartz定时任务(支持任务信息写入mysql,多节点分布式执行任务),下单成功后,生成一个30分钟后运行的任务,30分钟后检查订单状态,如果未支付,则进行处理
解决方案3: 
     *  将订单过期时间信息写入mysql,按分钟轮询查询mysql,如果超时则进行处理,效率差!时间精准度底!

原网址: 访问
创建于: 2021-02-04 17:14:06
目录: default
标签: 无

请先后发表评论
  • 最新评论
  • 总共0条评论