订单超时取消
在电商里面,如果用户下单后一直没有支付,这个订单就会被取消,从而释放库存。
订单超时取消在行业里有很多种做法,在这里仅介绍使用消息队列的解决方案。
需要使用延时消息,在发送者发送以后,一段时间后,消费者才能消费的消息。
消息队列也可以用于订单超时取消这种场景,在这种场景下,可以准备一个延时队列,比如超时时间是30min,延时就是30min。
在消费的时候要注意并发问题,也就是在30分钟这一时刻,一边用户支付,一边消费者也消费超时消息,就会有并发问题。解决思路有很多,比如用分布式锁、乐观锁,也可以使用SELECT FOR UPDATE锁住订单,防止并发操作。
在解决并发问题的思路里,提到了数据库部分的SELECT FOR UPDATE 和 乐观锁。
乐观锁方案简单来说就是把订单更新为超时状态的时候,需要确保原始状态是未支付。
UPDATE `order` SET `status`="超时未支付"
WHERE `id`=123 AND `status`="未支付"
在支付那边也要确保只有status
是未支付的时候才能发起支付。
备注:目前主流的消息队列里RocketMQ是支持延迟消息的,有插件。但是Kafka不支持。所以当面试官问你“为什么不用 Kafka”这种问题,你可以把Kafka 不支持延时消息作为理由之一。
亮点
回答为什么一定要使用消息队列?也就是不用消息队列会怎么样,用了有什么好处?
从创建订单的场景看,在订单创建后,要通知很多下游,常见的做法是发送一个订单创建的消息,然后关心订单创建的业务方各自去订阅这个消息。
-- 那为什么订单服务不直接调用各个业务方呢?
-- 类似的场景还有,在消息通讯里,为什么服务端不直接把消息转发给各个接收者呢?
本质问题是:在这个业务场景下,不异步、不解耦或不削峰会有什么问题?
答案是:性能差、扩展性差、可用性差
不太好的回答就是耦合严重,面试官希望深入解释的是耦合严重会带来什么后果。
同步调用方案相比引入消息队列来说有三个缺陷,分别是性能差、可扩展性差和可用性差
性能差
性能差是因为你需要停下来等全部调用完成才可以返回响应。
业务方必须停下来等待结果,如果我这里需要通知三个下游,那么就需要发起三次调用,并且等它们各自的结果返回之后才能继续往下执行,或者返回响应,这样性能太差了。
紧接着面试官就可能和你抬扛:“如果我并发调用呢?性能也很好啊!”他隐含的意思就是你可以开启多个线程或者协程,并发调用所有的下游。
但是,即便是并发调用性能也比使用消息队列差。
并发调用相比于使用消息队列,性能也更差。在并发调用的情况下,性能取决于最坏的那个同步调用什么时候返回结果。而正常我们丢一个消息到消息中间件上是很快的。
紧接着你可以补充一点,引出扩展性和可用性的话题。
并且,即便并发调用的性能损耗是可以接受的,但是扩展性和可用性还是解决不了。