业务场景:
项目中需要定时发布商品到第三方接口平台
分析实现方案
-
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了。
至于大家有更好的方法可以告诉我,小菜鸟一枚,只有这个简单的方法实现了。