天天跟我做,每天十分钟
- 前言(〃・ิ‿・ิ)ゞ
- 踩坑坑 (☄ฺ◣ω◢)☄ฺ
- 爬坑坑 ( ⁼̴̀ .̫ ⁼̴́ )✧
- 搞事事(ˊo̴̶̷̤ ᴗ o̴̶̷̤ˋ)
- 大功告成₍˄·͈༝·͈˄₎ฅ˒˒
前言(〃・ิ‿・ิ)ゞ
最近在和订单打交道,其中关于过期这一块费了不少时间,分享出来给大家作为一种思路。
踩坑坑 (☄ฺ◣ω◢)☄ฺ
下单、支付、取消都很容易解决,但是最后竟然漏了最重要的订单过期!
根据公司业务的不同,订单过期涉及到返还库存or解锁冻结金额
最初的想法是写一个定时器去数据库轮询所有状态是未付款的订单,就类似于下面的亚子
/**
* 每分钟查询一次数据库未付款的订单
*/
@Scheduled(fixedDelay = 60000)
public void query_order() {
//外币八部 业务逻辑
}
但是!刚写完就发现了问题!(( ◞•̀д•́)◞⚔◟(•̀д•́◟ ))
在数据量大的时候轮询数据库是及其耗费时间的!
并且如果订单在这一分钟之内过期了,库存并没有及时的返回,在限时秒杀这种业务场景下是极其恐怖的,友商可能重复下一些无效订单导致真实用户购买不到商品
转换一下思路,订单过期的时候不是我去找订单,而是订单主动来找我
爬坑坑 ( ⁼̴̀ .̫ ⁼̴́ )✧
订单是存在数据库的,数据库不可能主动来找我,这里提供两种方案:
一、
通过消息队列(RabbitMQ、RocketMQ)延迟队列来解决,发送消息的时候设置一个消费(过期)时间,到了过期时间消费者自动获取这条消息来进行业务处理。
二、
通过Redis来进行订单过期时间记录,下订单的时候给Key设置一个过期时间,系统监听过期的Key就可以了,我们这里就用第二种实现方式
搞事事(ˊo̴̶̷̤ ᴗ o̴̶̷̤ˋ)
首先配置一下Redis监听容器
@Configuration
public class RedisConfiguration {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
/**
* 初始化Redis监听容器
* @return
*/
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
return redisMessageListenerContainer;
}
/**
* 创建一个Key监听器并且绑定到容器中
* @return
*/
@Bean
public KeyExpiredListener keyExpiredListener() {
return new KeyExpiredListener(this.redisMessageListenerContainer());
}
}
之后创建一个类继承KeyExpirationEventMessageListener并重写onMessage方法
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
/**
* 监听所有失效的Key
* @param message Key
* @param pattern 不知道是个什么东东 父类也没有使用这个参数
*/
@Override
public void onMessage(Message message, byte[] pattern) {
System.out.println("过期的Key:" + message.toString());
}
}
大功告成₍˄·͈༝·͈˄₎ฅ˒˒
去Redis设置一个临时Key测试一下监听器是否正常
这里就大功告成了づ♡ど
但是有一个注意事项:
监听到的Key得不到里面的值,所以一般把数据放在Key上,例如订单号
然后我们根据这个订单号进行库存、金额的操作就非常及时了,性能也很不错’◡’