redis监听过期Key,同时获取过期的value,springboot整合redis 真实定时任务业务场景

业务场景:

项目中需要定时发布商品到第三方接口平台

分析实现方案

  • mq的消息延迟(我整合pulsar,不知道为什么消费者报错,其他的mq消息中间件也行)

  • 定时任务(如果没一个定时发布商品就生产一个定时任务的话,我个人觉得任务太多了,而且我们是分钟级别的扫描,不管是用redis缓存,还是数据库对我们的性能应该影响比较大)

  • redis的监听过期key(我目前使用的是这样的方法,但是看到很多博客都说onMessage是同步的,如果大量key失效的情况下,不知道会不会出问题,所有我还是建议消息中间件,而且对消息的丢失也有一定处理方案(死信队列)

上代码(代码是摘抄网上一些大佬的,能用就行,给自己做个笔记)
1.依赖

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-redis</artifactId>
       <version>2.3.8.RELEASE</version>
   </dependency>

2.配置类(贴心的为大家把序列化也做了)

package com.zrt.demo.redis;

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericToStringSerializer;

/**
* @program: SpringCloud
* @description: redis配置类
* @author: zhang yi
* @create: 2020-03-24 14:17
*/
@Configuration
public class RedisConfiguration {

   @Autowired
   private RedisConnectionFactory redisConnectionFactory;

   @Bean
   public RedisMessageListenerContainer redisMessageListenerContainer() {
       RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
       redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
       return redisMessageListenerContainer;
   }

   @Bean
   public KeyExpiredListener keyExpiredListener() {
       return new KeyExpiredListener(this.redisMessageListenerContainer());
   }

   @Bean
   public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
       RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
       redisTemplate.setConnectionFactory(redisConnectionFactory);
       // 使用 GenericFastJsonRedisSerializer 替换默认序列化
       GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
       // 设置key和value的序列化规则
       redisTemplate.setKeySerializer(new GenericToStringSerializer<>(Object.class));
       redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
       // 设置hashKey和hashValue的序列化规则
       redisTemplate.setHashKeySerializer(new GenericToStringSerializer<>(Object.class));
       redisTemplate.setHashValueSerializer(genericFastJsonRedisSerializer);
       // 设置支持事物
       redisTemplate.setEnableTransactionSupport(true);
       redisTemplate.afterPropertiesSet();
       return redisTemplate;
   }
}

3.监听器

package com.zrt.demo.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

/**
* @program: SpringCloud
* @description: redis Key过期监听
* @author: zhang yis
* @create: 2020-03-24 14:14
*/
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
   /**
    * 模拟自己的业务层代码
    */
   @Autowired
   RedisService service;

   public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
       super(listenerContainer);
   }

   @Override
   public void onMessage(Message message, byte[] pattern) {
       System.out.println("过期key:" + message.toString());
       //这个地方可以根据自己的业务来监听你锁需要的key
      if (message.toString().startsWith("item")){
         //业务调用
          service.redisKeyService(message.toString());
          System.out.println("进行发布逻辑");
      }
   }
}

4.模拟业务

package com.zrt.demo.redis;

import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.List;

/**
* @Author: zqf
* @Date: 2021/12/06/23:40
* @Description:
*/
@Service
public class RedisService {

   @Autowired
   RedisUtil redisUtil;

   /**
    * 模拟业务设置key
    */
   public void setRedisKey(){
       //注意想要拿到value,直接通过监听key是只能拿到key的值的,所有这里我另外设置了一个key,作为过期key的value使用
       long timeMillis = System.currentTimeMillis();
       for (int i = 0; i < 5; i++) {
           String key1 = "item_"+timeMillis+i;
           String key2 = "itemDetails_"+timeMillis+i;
           //设置10秒,模拟业务的定时任务周期
           redisUtil.set(key1,"1",10);
           //key2不设置过期时间(但是要注意redis的清除策略)
           redisUtil.set(key2,"商品信息");
       }

       System.out.println("redis设置key成功");
   }

   /**
    * 模拟业务获取value的操作
    */
   public void redisKeyService(String keyNmae){
       //切割,这里为了方便处理业务,我们前面设置了相同的后缀
       String[] strings = keyNmae.split("_");
       List<String> list = Arrays.asList(strings);
       String key ="itemDetails_" + list.get(1);
       if (redisUtil.hasKey(key)){
           String s = JSONObject.toJSONString( redisUtil.get(key));
           System.out.println("开始处理业务业务"+s);
           //最后删除key
           redisUtil.del(keyNmae);

       }
   }
}

5.控制层

package com.zrt.demo.controller;

import com.zrt.demo.redis.RedisService;
import com.zrt.demo.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: zqf
 * @Date: 2021/12/06/22:50
 * @Description:
 */
@RequestMapping("/redis")
@RestController
public class RedisController {

    @Autowired
    private RedisService redisService;
    @GetMapping("/set")
    public String demo(){
        redisService.setRedisKey();
        return "redis保存成功";
    }

}

6.控制台输出
在这里插入图片描述

总结

最后可以看到外面控制台成功的监听到过期key了,拿到对应的key了。
至于大家有更好的方法可以告诉我,小菜鸟一枚,只有这个简单的方法实现了。

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
对于 Redis过期回调,你可能会遇到一些坑。以下是一些常见的问题和解决方法: 1. 过期回调不触发:如果设置了过期时间,并且配置了过期回调函数,但回调函数没有被触发,可能是由于以下原因: - Rediskeyspace notifications 没有开启。可以通过配置文件或者使用 `CONFIG SET notify-keyspace-events` 命令来进行设置。 - 过期键的回调事件被禁用。可以通过配置文件或者使用 `CONFIG SET notify-keyspace-events` 命令来进行设置。 2. 回调函数执行的时间限制:Redis 对于过期回调函数有一个执行时间限制,默认是 100 毫秒。如果回调函数执行时间超过此限制,可能会被中断。为了避免此问题,可以将回调函数的逻辑简化,或者将复杂的处理逻辑转移到其他地方。 3. 回调函数的线程安全性:Redis过期回调函数是在主线程中运行的,所以要确保回调函数是线程安全的。需要考虑多线程并发访问共享资源时可能出现的竞争条件和数据一致性问题。 4. 回调函数的执行顺序:当多个键同时过期时,无法保证回调函数的执行顺序。如果需要按照某种顺序执行回调函数,可以在回调函数中添加额外的逻辑或者使用其他方法来进行控制。 5. 回调函数的效率:过期回调函数在 Redis 主线程中执行,可能会影响 Redis 的响应速度。如果回调函数执行时间过长或者频繁触发回调导致性能问题,可以考虑优化回调函数的逻辑或者使用其他解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值