星驰编程网

免费编程资源分享平台_编程教程_代码示例_开发技术文章

Spring Boot 3 中实现 30 分钟自动取消订单操作全解析

在当今的互联网软件开发领域,尤其是涉及到在线支付的应用场景中,实现订单在一定时间内未支付则自动取消的功能至关重要。以电商平台为例,如果用户下单后长时间不支付,订单所占用的库存资源无法及时释放,会影响其他用户的购买体验,也不利于商家的库存管理。对于在线订票系统而言,未支付订单长时间占据票源,同样会给其他用户带来不便。在众多的开发框架中,Spring Boot 以其便捷性和强大功能深受开发者喜爱,今天我们就来深入探讨如何在 Spring Boot 3 中实现 30 分钟自动取消订单的操作。

定时任务方案

在 Spring Boot 3 中,利用 @Scheduled 注解可以轻松实现定时任务。这个任务将周期性地扫描数据库,检查未支付的订单,若订单生成时间超过 30 分钟仍未支付,则自动取消订单。

1.1 代码实现示例

@Component
public class OrderCancelTask {
    @Autowired
    private OrderService orderService;
    @Scheduled(cron = "0 0/1 * * *?")
    public void cancelUnpaidOrders() {
        LocalDateTime expirePoint = LocalDateTime.now().minusMinutes(30);
        List<Order> unpaidOrders = orderService.getUnpaidOrders();
        for (Order order : unpaidOrders) {
            if (order.getCreationTime().isBefore(expirePoint)) {
                orderService.cancelOrder(order.getId());
            }
        }
    }
}

在这段代码中,@Component 注解表明这个类是 Spring 应用中的一个组件,会被 Spring 容器管理。@Autowired 注解实现了 OrderService 类型对象的自动注入,方便在本类中使用其方法。@Scheduled(cron = "0 0/1 * * *?") 定义了定时任务的执行计划,这里的 cron 表达式表示任务将每分钟执行一次。在 cancelUnpaidOrders 方法内,首先计算出 30 分钟前的时间点 expirePoint,然后通过
orderService.getUnpaidOrders() 获取所有未支付的订单列表。遍历该列表,对于每个订单,检查其创建时间是否早于 expirePoint,如果是,则调用 orderService.cancelOrder(order.getId()) 取消订单。

1.2 方案优缺点

优点

  • 实现简单直接,对于业务逻辑相对简单的系统来说,容易理解和维护。
  • 不需要额外引入复杂的中间件,只依赖 Spring Boot 自身的功能即可。

缺点

  • 定时扫描数据库会对数据库造成一定压力,尤其是在订单数据量较大时,频繁的查询操作可能影响数据库性能。
  • 存在一定的延迟性,由于是周期性扫描,例如设置每分钟扫描一次,那么订单实际可能最长会在 1 分钟后才被取消,对于一些对时间精度要求较高的场景不太适用。

延迟队列方案

使用消息队列(如 RabbitMQ)的延迟队列功能来实现订单 30 分钟自动取消也是一种常见的方案。当订单生成时,将订单 ID 推送到延迟队列,并设置 30 分钟后过期。过期后,消息被消费,执行取消订单的操作。

2.1 代码实现示例

// 订单创建
@Service
public class OrderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void createOrder(Order order) {
        // 保存数据库
        saveOrder(order);
        // 将订单ID推送至延迟队列
        rabbitTemplate.convertAndSend("orderDelayExchange", "orderDelayKey", order.getId(), message -> {
            message.getMessageProperties().setDelay(30 * 60 * 1000);
            return message;
        });
    }
}
// 订单延迟消费
@Component
public class OrderDelayConsumer {
    @Autowired
    private OrderService orderService;
    @RabbitHandler
    @RabbitListener(queues = "orderDelayQueue")
    public void cancelOrder(String orderId) {
        orderService.cancelOrder(orderId);
    }
}

在订单创建部分,OrderService 中的 createOrder 方法在保存订单到数据库后,通过
rabbitTemplate.convertAndSend 方法将订单 ID 发送到名为 orderDelayExchange 的交换器,路由键为 orderDelayKey。并且通过
message.getMessageProperties().setDelay(30 * 60 * 1000) 设置了消息延迟 30 分钟。在订单延迟消费部分,OrderDelayConsumer 类通过 @RabbitListener(queues = "orderDelayQueue") 注解监听名为 orderDelayQueue 的队列,当接收到消息(即订单 ID)时,@RabbitHandler 注解标记的 cancelOrder 方法被调用,进而执行取消订单的操作。

2.2 方案优缺点

优点

  • 解耦性好,订单创建和订单取消的逻辑通过消息队列进行了分离,使得系统的模块间耦合度降低,更易于维护和扩展。
  • 延迟时间相对精准,能够更准确地在 30 分钟到期时触发取消订单操作,减少了像定时任务那样的扫描延迟。

缺点

  • 需要引入消息队列中间件,增加了系统的复杂性和运维成本。例如需要对 RabbitMQ 进行安装、配置、集群搭建等操作,并且要确保其高可用性。
  • 增加了系统的学习成本,开发人员需要熟悉消息队列的相关知识,包括队列、交换器、路由等概念,以及如何在 Spring Boot 中与之集成。

Redis 过期事件方案

利用 Redis 的键过期事件功能,当订单生成时,在 Redis 中存储一个键,设置 30 分钟过期。键过期时,通过 Redis 的过期事件通知功能触发订单取消操作。

3.1 代码实现示例

@Service
public class OrderService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    public void createOrder(Order order) {
        // 保存订单至数据库
        saveOrder(order);
        // 在redis中存储一个key,设置30分钟过期
        redisTemplate.opsForValue().set("order:" + order.getId(), order.getId(), 30, TimeUnit.MINUTES);
    }
    // 当key过期时,redis会自动调用该方法(需要配置redis的过期事件通知)
    public void onOrderKeyExpired(String orderId) {
        cancelOrder(orderId);
    }
}

在 OrderService 的 createOrder 方法中,保存订单到数据库后,通过 redisTemplate.opsForValue().set("order:" + order.getId(), order.getId(), 30, TimeUnit.MINUTES) 在 Redis 中存储一个键,键名为 order:订单ID,值也为订单 ID,并设置 30 分钟过期。当该键过期时,Redis 会自动调用 onOrderKeyExpired 方法(前提是配置了 Redis 的过期事件通知),在这个方法中执行取消订单的操作。

3.2 Redis 过期事件配置

要使用 Redis 的过期事件通知功能,需要进行以下配置:

确保 Redis 的配置文件(通常是 redis.conf)中开启了键空间通知功能。可以通过在配置文件中添加或修改如下配置实现:

notify - keyspace - events Ex

这里的 "Ex" 表示只监听键过期事件。如果需要监听其他类型的事件,可以参考 Redis 官方文档进行配置。

在 Spring Boot 应用中,使用
RedisMessageListenerContainer 来订阅 Redis 的键过期事件,并指定回调方法进行处理。示例代码如下:

@Configuration
public class RedisConfig {
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message, byte[] pattern) {
                String expiredKey = message.toString();
                // 处理订单超时逻辑,例如调用OrderService的取消订单方法
            }
        }, new PatternTopic("__keyevent@*__:expired"));
        return container;
    }
}

在这个示例中,__keyevent@*__:expired 是一个模式匹配的主题,它可以匹配所有数据库的键过期事件。当一个键过期时,onMessage 方法会被调用,在方法内可以加入处理订单超时取消的逻辑。

3.3 方案优缺点

优点

  • 性能高效,Redis 是基于内存的数据库,操作速度快,键过期事件的触发和处理相对及时,能够满足高并发场景下对订单取消及时性的要求。
  • 减轻数据库压力,不需要像定时任务那样频繁查询数据库来判断订单是否超时,只在订单创建和取消时与数据库交互,降低了数据库的负载。

缺点

  • 依赖 Redis,需要确保 Redis 服务的稳定性和高可用性。如果 Redis 出现故障,可能导致订单取消功能无法正常工作,因此需要对 Redis 进行集群部署和监控等操作。
  • 配置相对复杂,不仅需要在 Redis 中开启相关配置,还需要在 Spring Boot 应用中进行详细的事件监听配置,对开发人员的要求较高。

方案选择建议

在选择实现 30 分钟自动取消订单的方案时,需要综合考虑多种因素。如果你的系统订单数据量较小,对性能和时间精度要求不是特别高,并且希望实现简单、易于维护,那么定时任务方案是一个不错的选择。若系统注重模块间的解耦,对延迟时间的精准度有较高要求,且开发团队对消息队列技术有一定的掌握,延迟队列方案会更合适。而当系统面临高并发场景,对性能要求极高,并且已经在使用 Redis 作为缓存等用途时,Redis 过期事件方案能够充分发挥 Redis 的优势,是较为理想的选择。

在实际的互联网软件开发过程中,实现订单自动取消功能只是众多业务需求中的一个环节。但通过对这一功能的深入研究和合理实现,可以提升整个系统的用户体验和资源利用率。希望本文所介绍的在 Spring Boot 3 中实现 30 分钟自动取消订单操作的三种方案,能够为广大互联网软件开发人员在实际项目中提供有益的参考和帮助。在不断的技术探索和实践中,我们能够打造出更加高效、稳定和用户友好的互联网应用系统。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言