微软一面:订单超时未支付,如何自动关闭?

hi,你好,我是猿java

最近,有小伙伴私信反馈微软一面的系统设计题:订单超时未支付,如何自动关闭?

说实话,微软能问出这种面试题确实很诧异,难道是互联网人已经大量涌进了微软这个养老基地,开始卷它?言归正传,有网购经验的小伙伴应该知道,如果订单在规定的时间内没有支付,订单就会被系统自动关闭,作为技术人员,该如何设计这个功能,今天我们来分析 4种有概括性的方案。

说明:

  • 规定的时间:即超时时间,这个因平台而异,有的场景是 10分钟超时,比如 12306 购票,有的平台是 15分钟或者 30分钟超时,有些秒杀场景是 1分钟超时,具体超时值需要根据具体的业务来定。

  • 自动关闭的原因:对于有库存的业务,需要释放库存,否则容易导致少卖。比如有些用户恶意下单,不支付。

方案1: 定时器

定时器应该是我们最可能想到的方案,通过定时器去数据库轮询待支付的订单信息,然后在业务代码中判断订单是否超时,更新订单状态。

实现思路

下单的时候把超时需要关闭订单的具体时间值(pay_expire_time)保存到表里,给pay_expire_time字段添加一个索引,然后设置一个定时器,每 3s/5s 去数据库查询一次数据,查询 SQL 如下:

1
select id from order where order_status = '待支付' && pay_expire_time > now()

优点

定时器方案最大的优点就是实现简单

缺点

  1. 需要定时从数据库拉取数据,对数据库会产生一定的压力
  2. 需要将数据加载到内存中进行业务逻辑处理,如果拉取的数据量比较大,可能会消耗过多的内存导致OOM
  3. 定时器是周期性的,对于超时订单的关闭可能存在延时

适用场景

适用于数据量比较小的业务场景,比如,很多小公司在业务处于 0-1 的过程中,该方案比较常见。

方案2: 被动关闭

服务器不做订单的主动关闭,而是在客户端拉取订单时按需被动关闭订单,比如,客户端通过接口获取服务器的时间以及订单超时时间,然后计算并判断订单是否超时,如果超时,将订单显示成超时的样式,
并隐式向服务器发送一个订单超时关闭的请求,服务器再做相应的逻辑处理。

优点

  • 实现简单
  • 按需处理订单超时,客户端需要用到数据再做判断

缺点

  • 订单长时间未关闭,在业务意义上可能是脏数据
  • 客户端需要做超时的逻辑判断并且发送超时关闭请求

适用场景

适用于数据量比较小的业务场景,被动关闭通常会结合定时器方案使用。

方案3: MQ延时消息

有些 MQ有延时消息的特性,比如阿里开源的 RocketMQ,用户创建订单时,同时往 MQ中发送一条延时消息,用户支付成功后,删除对应的延时消息(开源版本的没有此功能),
当消费者消费到消息时再做超时的相关处理

优点

  • 利用中间件 MQ的特性,避免业务端自己实现

缺点

  • 如果使用开源的 Apache RocketMQ 4.x版本,消息延时只有特定的 18个级别,所以业务的超时时间要和 RocketMQ的延时级别相匹配才能使用
  • 强依赖中间件 MQ,所以 MQ的稳定性以及是否丢消息直接影响该功能
  • 使用开源版的 RocketMQ,没有删除延时消息的功能,因此需要对每条消息都做超时判断,增加了很多无效的数据处理
  • 如果数据量比较大,MQ可能会堆积产生延时

注意:

在 RocketMQ 4.x中 只有 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 18个固定Level的延迟时间

在 RocketMQ 5.x,支持任意时间的延迟消息,官方解决的 issue

适用场景

适用于搭建了 RocketMQ的公司,想利用 RocketMQ的延时消息来实现该功能

方案4: 超时中心

很多大厂都有自己的分布式超时系统 TOC(Timeout Coordinator System),不同的公司可能叫法不一样,这里给出一种实现思路:

实现思路

1. 订单创建

  • 用户通过前端下单,请求发送到OMS(Order Management System)订单管理系统
  • OMS创建订单,并将订单状态设置为“待支付”或类似的初始状态

2. 注册超时事件

  • 在订单创建的同时,OMS向 TOC注册一个超时事件
  • 超时事件包含了订单ID、超时时间(例如30分钟)和超时后需要执行的动作(例如关闭订单)

3. 超时监控

  • TOC系统开始监控这个订单的超时事件
  • 如果在设定的超时时间内,用户完成了支付,OMS会更新订单状态并通知 TOC取消超时事件

4. 超时处理

  • 如果超时时间到达,而订单状态仍然是“待支付”,TOC将触发超时动作
  • TOC通知 OMS订单已超时

5. 订单关闭

  • OMS收到 OTC超时通知后,执行订单关闭操作
  • OMS将订单状态更新为“已关闭”或“超时关闭”

6. 超时后处理

  • OMS可能需要处理一些后续任务,如释放库存、退还优惠券等
  • 完成这些操作后,该订单的超时处理流程结束

优点

  • 专门的人做专门的事,性能,稳定性,扩展性有保障
  • 可以处理海量数据

缺点

  • 实现和维护成本比较高
  • 实现复杂度比较高,方案中可能会涉及分布式调度,MQ等技术栈

适用场景

适合数据量比较大的中大型公司,需要专门的团队负责

如何选择?

架构领域有句经典的台词:没有最好的架构,只有够用的架构。

这个理论用于订单超时关闭场景同样适用,分布式超时中心方案看起来高大上,而且能处理海量数据,但是,如果把这套方案用于几个人的团队,有一种杀鸡用牛刀的架势,可能不但解决不了问题,还会给团队带来技术难题和成本负担。

因此,架构一定要基于业务,方案也一定要基于业务。

对于定时器方法,看起来简单粗暴,但是很多公司的业务在 0-1的过程中,它就是首选,因为实现的成本很低,没有什么技术难点,
假设每条订单的数据大小是 1K,5万条订单的数据大概在 50M左右,用定时器把数据加载到内存中处理也是ok的,所以该方案适合每天的订单量在万级别,或者再保守一些,每日订单总量在 1万以下。

对于 MQ延时消息方案,首先需要考虑自建 MQ 还是购买 MQ云产品,以及两种方案带来的技术难点或者购买成本,基于先有 MQ这个条件再看它适合的业务场景,在根据每日的订单量是百万级别,十万级别还是万级别来选择 MQ的配置。

分布式超时中心方案技术难度和维护成本都比较高,一般适合一些独角兽企业,大厂或者财力和技术能力都不错的公司。

总结

本文分了具有概括性的 4种订单超时关闭方案,可能每个公司实现的细节或者叫法略有差异,但是思想是相通的。

通过本文,我们可以根据不同的业务体量给出不同的方案或者设计思路,以及各个方案的优缺点,具体细节可以结合具体业务细节。

没有最好的架构,只有够用(适合)的架构,很多时候在选择方案的时候一定要结合实际业务,切勿过度设计,但是也不能缺乏设计的能力

交流学习

如果文章存在缺点和错误,欢迎批评指正。更多干货和面试经,关注公众号:猿java。

drawing