一、订单查询时检查是否要关闭
实现方式
在订单查询接口中,每次用户访问订单时,系统检查该订单是否已过期,若过期则执行关闭操作。
Java代码示例
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order getOrder(String orderId) {
Order order = orderRepository.findById(orderId);
if (order != null && order.isExpired()) {
closeOrder(order);
}
return order;
}
private void closeOrder(Order order) {
// 关闭订单逻辑,如更新数据库状态等
order.setStatus(OrderStatus.CLOSED);
orderRepository.save(order);
}
}
优缺点分析
-
优点:实现简单,无需额外开发定时任务或消息队列。
-
缺点:
-
- 脏数据问题:如果用户长期不访问订单,过期订单将一直存在于数据库中。
- 性能影响:每次查询订单都进行写操作,可能增加数据库负担。
- 实时性差:依赖于用户访问订单的时间。
**
**
适用场景
适用于学习或小规模、实时性要求不高的系统。
二、xxl-job定时任务
实现方式
使用xxl-job等调度框架,设置定时任务定期扫描数据库中过期的订单,并执行关闭操作。
Java代码示例
@Component
public class ExpiredOrderJob {
@Autowired
private OrderService orderService;
@XxlJob("closeExpiredOrders")
public void execute() throws Exception {
List<Order> expiredOrders = orderService.findExpiredOrders();
for (Order order : expiredOrders) {
orderService.closeOrder(order);
}
}
}
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public List<Order> findExpiredOrders() {
return orderRepository.findByStatusAndExpireTimeLessThan(OrderStatus.CREATED, LocalDateTime.now());
}
// closeOrder方法同上
}
优缺点分析
-
优点:实现简单,定时执行,易于管理。
-
缺点:
-
- 时间不精准:基于固定频率执行,可能导致订单关闭时间晚于预期。
- 数据库压力:大量订单时,集中扫描数据库可能对性能造成影响。
- 分库分表问题:在分库分表环境下,全表扫描效率极低。
适用场景
适用于对时间精度要求不高、订单量适中的系统。
三、RocketMQ延迟消息
实现方式
在订单创建时,发送一个延迟消息到RocketMQ,消息延迟时间与订单过期时间一致。消息到期后,消费者接收到消息并执行关闭订单操作。
Java代码示例
@Autowired
private RocketMQTemplate rocketMQTemplate;
public void createOrderAndSendDelayMessage(Order order) {
// 创建订单逻辑
// ...
// 发送延迟消息
int delayLevel = calculateDelayLevel(order.getExpireTime()); // 根据过期时间计算延迟等级
rocketMQTemplate.convertAndSend("delayTopic", "tag", order, message -> {
message.setDelayTimeLevel(delayLevel);
return message;
});
}
@RocketMQMessageListener(consumerGroup = "orderConsumerGroup", topic = "delayTopic")
public class OrderConsumer implements RocketMQListener<Order> {
@Autowired
private OrderService orderService;
@Override
public void onMessage(Order order) {
orderService.closeOrder(order);
}
}
优缺点分析
-
优点:系统解耦,延迟消息处理灵活。
-
缺点:
-
- 延迟时间固定:RocketMQ支持的延迟时间有限,不够灵活。
- 依赖外部消息队列,增加系统复杂度。
适用场景
适用于订单量较大、对时间精度有一定要求,且延迟时间能与RocketMQ支持的延迟级别匹配的场景。
四、Redis消息自动过期和notify-keyspace-events事件监听
实现方式
利用Redis的键过期特性和notify-keyspace-events事件监听功能,在redis的redis.conf里加入notify-keyspace-events的配置,开启key事件通知功能。然后订单创建时在Redis中设置一个键,并设置过期时间。在代码里要实现一个redis key过期的监听器,键过期时,监听器接收到过期事件,执行关闭订单操作。
Java代码示例
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
@Autowired
private OrderService orderService;
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
String expiredKey = message.toString();
// 假设订单ID作为Redis键
orderService.closeOrderById(expiredKey);
}
}
@Service
public class OrderService {
// closeOrderById方法根据订单ID关闭订单
}
// Redis配置(略)
优缺点分析
-
优点:
-
- 实时性好:订单过期即处理。
- 效率高:Redis性能优异。
-
缺点:
-
- Redis配置复杂:需要开启notify-keyspace-events。
- 集群部署时扩展性较差。
适用场景
适用于订单量适中、对实时性要求高、系统架构较简单的场景。
五 CaffeineCache
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.removalListener((key, value, cause) ->
System.out.println("Key " + key + " expired. Do something here."))
.build(key -> createExpensiveGraph(key));
在这个示例中,我们创建了一个LoadingCache并设置了键的写入过期时间为1分钟。当键过期时,会触发removalListener中的操作,您可以在这里执行相应的操作。
五、总结
每种过期订单关闭方案都有其独特的优缺点和适用场景。选择哪种方案,需要根据实际业务需求、系统规模、团队技术栈以及对实时性的要求综合考虑。例如,在订单量较小、实时性要求不高的场景下,可以选择订单查询时检查;在订单量适中、对时间精度有一定要求的场景下,xxl-job定时任务是一个不错的选择;而在订单量较大、对实时性要求极高的场景下,RocketMQ延迟消息或Redis消息自动过期方案可能更为合适。总之,没有绝对的最佳方案,只有最适合当前业务场景的方案。