利用redis实现订单倒计结束后更改订单状态为已失效
利用Redis实现订单倒计时并自动失效订单的功能,可以通过Redis的过期键通知(Key Expiration Notification)和定时任务补偿机制实现
实现步骤
- 配置Redis启用过期事件
修改Redis配置文件 redis.conf,开启键过期事件通知:
启用过期事件通知
notify-keyspace-events Ex
并且启用Redis的持久化
save 900 1
save 300 10
save 60 10000
重启Redis服务使配置生效。
- Java监听Redis过期事件
使用Spring Data Redis或Jedis监听键过期事件。
示例代码(Spring Data Redis):
package com.transport.framework.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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;
/**
* redis配置
*
* @author transport
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisConnectionFactory connectionFactory,
OrderExpirationListener orderExpirationListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 订阅所有数据库的过期事件(0表示数据库编号)
container.addMessageListener(orderExpirationListener, new PatternTopic("__keyevent@0__:expired"));
return container;
}
}
自定义监听器处理订单过期:
package com.transport.framework.config;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;
@Component
public class OrderExpirationListener implements MessageListener {
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
// 解析订单ID(假设key格式为order:订单ID)
if (expiredKey.startsWith("order:")) {
String orderNo = expiredKey.split(":")[1];
// 调用订单服务更新订单状态为失效
System.out.println("已失效的订单编号为:"+orderNo);
}
}
}
订单服务更新状态:
代码省略(注意:只有未支付的订单才更新状态)
3. 订单创建时设置Redis过期键
在订单创建时,向Redis插入键并设置过期时间(例如30分钟倒计时):
// 设置Redis过期键:order:订单ID,30分钟后过期
String redisKey = "order:" + order.getId();
redisTemplate.opsForValue().set(redisKey, "1", Duration.ofMinutes(30));
4. 兜底方案:定时任务补偿
为防止Redis事件丢失,增加定时任务扫描数据库中未过期的订单:
@Scheduled(cron = "0 */5 * * * ?") // 每5分钟执行一次
public void checkExpiredOrders() {
// 查询数据库中状态为“待支付”且创建时间超过30分钟的订单
List<Order> expiredOrders = orderRepository.findExpiredOrders(OrderStatus.PENDING, LocalDateTime.now().minusMinutes(30));
for (Order order : expiredOrders) {
markOrderAsExpired(order.getId());
// 可选:删除Redis中的键(避免重复处理)
redisTemplate.delete("order:" + order.getId());
}
}