【Java】电商平台中订单未支付过期如何实现自动关单?

一、问题解析

📌

日常开发中,我们经常遇到这种业务场景,如:外卖订单超 30 分钟未支付,则自动取订单;用户注册成功 15 分钟后,发短信息通知用户等等。这就延时任务处理场景。

在电商,支付等系统中,一设都是先创建订单(支付单),再给用户一定的时间进行支付,如果没有按时支付的话,就需要把之前的订单(支付单)取消掉。这种类以的场景有很多,还有比如到期自动收货,超时自动退款,下单后自动发送短信等等都是类似的业务问题。

2.1 定时任务

通过定时任务关闭订单,是一种成本很低,实现也很容易的方案。通过简单的几行代码,写一个定时任务,定期扫描数据库中的订单,如果时间过期,就将其状态更新为关闭即可。

优点:实现容易,成本低,基本不依赖其他组件。

缺点:

时间可能不够精确。由于定时任务扫描的间隔是固定的,所以可能造成一些订单已经过期了一段时间才被扫描到,订单关闭的时间比正常时间晚一些。

增加了数据库的压力。随着订单的数量越来越多,扫描的成本也会越来越大,执行时间也会被拉长,可能导致某些应该被关闭的订单迟迟没有被关闭。

总结采用定时任务的方案比较适合对时间要求不是很敏感,并且数据量不太多的业务场景。

2.2 JDK 延迟队列 DelayQueue

DelayQueue 是 JDK 提供的一个无界队列,我们可以看到,DelayQueue 队列中的元素需要实现 Delayed,它只提供了一个方法,就是获取过期时间。

用户的订单生成以后,设置过期时间比如 30 分钟,放入定义好的 DelayQueue,然后创建一个线程,在线程中通过 while(true)不断的从 DelayQueue 中获取过期的数据。

优点:不依赖任何第三方组件,连数据库也不需要了,实现起来也方便。

缺点:

因为 DelayQueue 是一个无界队列,如果放入的订单过多,会造成 JVM OOM。

DelayQueue 基于 JVM 内存,如果 JVM 重启了,那所有数据就丢失了。

总结DelayQueue 适用于数据量较小,且丢失也不影响主业务的场景,比如内部系统的一些非重要通知,就算丢失,也不会有太大影响。

2.3 redis 过期监听

redis 是一个高性能的 KV 数据库,除了用作缓存以外,其实还提供了过期监听的功能。

在 redis.conf 中,配置 notify-keyspace-events Ex 即可开启此功能。

然后在代码中继承 KeyspaceEventMessageListener,实现 onMessage 就可以监听过期的数据量。

public abstract class KeyspaceEventMessageListener implements MessageListener, InitializingBean, DisposableBean {

  private static final Topic TOPIC_ALL_KEYEVENTS = new PatternTopic("__keyevent@*");

  //...省略部分代码
  public void init() {
    if (StringUtils.hasText(keyspaceNotificationsConfigParameter)) {
      RedisConnection connection = listenerContainer.getConnectionFactory().getConnection();
      try {
        Properties config = connection.getConfig("notify-keyspace-events");
        if (!StringUtils.hasText(config.getProperty("notify-keyspace-events"))) {
          connection.setConfig("notify-keyspace-events", keyspaceNotificationsConfigParameter);
        }
      } finally {
        connection.close();
      }
    }
    doRegister(listenerContainer);
  }

  protected void doRegister(RedisMessageListenerContainer container) {
    listenerContainer.addMessageListener(this, TOPIC_ALL_KEYEVENTS);
  }
  //...省略部分代码
  @Override
  public void afterPropertiesSet() throws Exception {
    init();
  }
}

通过以上源码,我们可以发现,其本质也是注册一个 listener,利用 redis 的发布订阅,当 key 过期时,发布过期消息(key)到 Channel :keyevent@*:expired 中。

在实际的业务中,我们可以将订单的过期时间设置比如 30 分钟,然后放入到 redis。30 分钟之后,就可以消费这个 key,然后做一些业务上的后置动作,比如检查用户是否支付。

优点: 由于 redis 的高性能,所以我们在设置 key,或者消费 key 时,速度上是可以保证的。

缺点:由于 redis 的 key 过期策略原因,当一个 key 过期时,redis 无法保证立刻将其删除,自然我们的监听事件也无法第一时间消费到这个 key,所以会存在一定的延迟。另外,在 redis5.0 之前,订阅发布中的消息并没有被持久化,自然也没有所谓的确认机制。所以一旦消费消息的过程中我们的客户端发生了宕机,这条消息就彻底丢失了。

总结:redis 的过期订阅相比于其他方案没有太大的优势,在实际生产环境中,用得相对较少。

2.4 Redisson 分布式延迟队列

Redisson 是一个基于 redis 实现的 Java 驻内存数据网格,它不仅提供了一系列的分布式的 Java 常用对象,还提供了许多分布式服务。

Redisson 除了提供我们常用的分布式锁外,还提供了一个分布式延迟队列 RDelayedQueue,他是一种基于 zset 结构实现的延迟队列,其实现类是 RedissonDelayedQueue。

优点:使用简单,并且其实现类中大量使用 lua 脚本保证其原子性,不会有并发重复问题。

缺点:需要依赖 redis(如果这算一种缺点的话)。

总结:Redisson 是 redis 官方推荐的 JAVA 客户端,提供了很多常用的功能,使用简单、高效,推荐大家尝试使用。

2.5 RocketMQ 延迟消息

延迟消息,当消息写入到 Broker 后,不会立刻被消费者消费,需要等待指定的时长后才可被消费处理的消息,称为延时消息。

在订单创建之后,我们就可以把订单作为一条消息投递到 rocketmq,并将延迟时间设置为 30 分钟,这样,30 分钟后我们定义的 consumer 就可以消费到这条消息,然后检查用户是否支付了这个订单。

通过延迟消息,我们就可以将业务解耦,极大地简化我们的代码逻辑。

优点:可以使代码逻辑清晰,系统之间完全解耦,只需关注生产及消费消息即可。另外其吞吐量极高,最多可以支撑万亿级的数据量。

缺点:相对来说 mq 是重量级的组件,引入 mq 之后,随之而来的消息丢失、幂等性问题等都加深了系统的复杂度。

总结:通过 mq 进行系统业务解耦,以及对系统性能削峰填谷已经是当前高性能系统的标配。

2.6 RabbitMQ 死信队列

除了 RocketMQ 的延迟队列,RabbitMQ 的死信队列也可以实现消息延迟功能。

当 RabbitMQ 中的一条正常消息,因为过了存活时间(TTL 过期)、队列长度超限、被消费者拒绝等原因无法被消费时,就会被当成一条死信消息,投递到死信队列。

基于这样的机制,我们可以给消息设置一个 ttl,然后故意不消费消息,等消息过期就会进入死信队列,我们再消费死信队列即可。

通过这样的方式,就可以达到同 RocketMQ 延迟消息一样的效果。

优点:同 RocketMQ 一样,RabbitMQ 同样可以使业务解耦,基于其集群的扩展性,也可以实现高可用、高性能的目标

缺点:死信队列本质还是一个队列,队列都是先进先出,如果队头的消息过期时间比较长,就会导致后面过期的消息无法得到及时消费,造成消息阻塞。

总结:除了增加系统复杂度之外,死信队列的阻塞问题也是需要我们重点关注的。

二、粉丝福利 

我根据我从小白到架构师多年的学习经验整理出来了一份50W字面试解析文档、简历模板、学习路线图、java必看学习书籍 、 需要的小伙伴斯我一下,或者评论区扣“求分享”    

 

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Java电商平台设计与实现是一个涉及到多方面技术的复杂任务。下面我将介绍一些关键方面的内容。 首先,电商平台需要有一个稳定的数据库系统来存储商品信息、订单信息以及用户信息。可以选择使用关系型数据库例如MySQL或者非关系型数据库例如MongoDB来实现。 其次,平台需要能够支持用户注册、登录、浏览商品、下单、支付等功能。这些功能通常需要涉及到使用Java Web框架来开发,并通过使用HTML、CSS和JavaScript等前端技术来实现用户界面。 另外,在电商平台支付系统是非常重要的一环,需要确保支付过程的安全性和可靠性。可以使用第三方支付平台例如支付宝或者微信支付实现支付功能。 还有,在电商平台,搜索功能是必不可少的。可以使用全文搜索引擎例如Elasticsearch来实现高效的商品搜索。 此外,电商平台还需要考虑到用户体验和性能优化等方面。可以使用CDN来缓存静态资源,使用分布式技术来提高系统的扩展性和可靠性。 最后,为了确保电商平台的安全性,需要实施严格的安全措施,例如使用HTTPS协议来保护用户的数据传输,使用防火墙和安全审计等技术来保护系统免受攻击。 总而言之,基于Java电商平台设计与实现需要综合运用数据库Java Web开发、前端技术、支付系统、搜索引擎、分布式技术和安全措施等多种技术来完成。这只是一个简要的概述,实际开发还需要深入研究和实践。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值