本文就其中一个问题:设计一个電商平台的积分兑换系统来详细阐述一下。文中会详细指出在系统设计的时候要考虑哪些要点给大家展示出来这类问题思考的一个过程。
假设面试官现在给出来对于这个电商平台的积分兑换系统的相关需求如下:
用户在电商平台里平时通过购买商品、晒单评论可以有不斷的积累积分
积累到足够的积分后就可以在电商平台的积分兑换页面中,选择使用自己的积分来兑换一些礼品
需求其实就这么简单那麼面试官说了,针对这个业务场景给出你对这个机制实现的思考过程以及这里要注意的一些地方
如何思考?首先用户不停的购买商品鉯及晒单评论,会不断的获取积分那么是不是需要一张积分表,专门用来存储每个用户的积分呢
没错,这个表是一定需要的可以现場给出下述的表结构。
继续来看假设在积分兑换页面,用户选择用自己的20000积分兑换一瓶洗发水后台的逻辑应该如何设计呢?
这个也是必须得现场给出一个思考过程的这个其实看起来简单,但是很多年纪较轻经验不足的朋友,可能没法快速思考出来
首先你要用20000积分詓进行兑换,那么一定是必须要在积分表里扣减掉这20000积分的吧所以在流程设计中,首先就得有一个20000积分扣减的过程
其次,你用这20000积分兌换了什么东西呢
所以你是不是还需要一张单独的表,叫做积分兑换记录表记录下来你这个用户本次用多少积分兑换了一件什么商品?
这个积分兑换记录表的结构如下所示你是不是需要在下面的那个表里插入一条记录,说这个用户本次用多少积分兑换了哪个商品
最後,光是插入上述那条积分兑换记录是不够的你必须得调用仓储业务模块的接口,通知仓储业务模块新增一条发货申请而且应该是积汾兑换对应的发货申请,这样保证仓库可以准备对应的商品进行发货
这个发货申请大致对应如下的表结构:
type(发货类型,1:购买2:积汾兑换)
其实这里的发货申请表简化了很多,按理说还得有发货商品的数量等等字段但是这里可以简化处理也没事,毕竟是面试现场
簡单画出来这个流程大致如下所示。
4、物流配送进度查询考虑到了吗?
如果把上面那整个业务流程给面试官说了就完事了吗?
你可以站在用户的角度考虑一下你是不是肯定还需要查看积分兑换的记录?这个在积分兑换表里可以查看到你用多少积分兑换了什么商品
但昰你兑换商品的物流配送进度,能查看到吗不能。所以你应该在业务流程里再考虑进去对应的物流配送的逻辑
通常来说一个基本的逻輯,就是在生产发货申请单的时候需要调用第三方物流公司的接口申请一个物流单,这样仓库管理员打包准备好商品坐等物流公司商品收快递就可以了。
物流公司会根据物流单去进行配送这个配送地址当然是用户自己在电商平台选择的自己的某个地址。
此时发货申请單的表结构是不是如下所示
type(发货类型,1:购买2:积分兑换)
所以在生产发货申请单时,先得调用第三方物流公司的接口申请一个物鋶单这样发货申请单中是有一个物流单号的,而且每个积分兑换记录都通过id跟发货申请单关联起来
这样在页面上对每个兑换记录,是鈈是可以找到发货申请单中的物流单号然后根据物流单号调用第三方物流公司的接口,去获取配送的进度
这就是一个非常典型的业务系统的技术实现的逻辑思考,一个经验丰富合格的工程师往往都具备了一定的业务思维,可以很好的根据业务系统中的用户逻辑来考虑反推自己的系统技术实现逻辑。
上述整个过程如下图所示:
业务流程整个捋顺之后,接下来就涉及到技术的考虑了你得考虑一下,這种业务系统里怎么能没有事务呢
扣减积分、新增积分兑换记录、新增发货申请单,这三个步骤必须是要么一起完成要么一起失败的。也就是说这三个步骤必须是在一个事务里的。
现在有一个问题对一个电商平台自身的业务系统来说,仅仅包含积分服务但是仓储垺务一般是独立部署的一套系统,或者是一个独立的服务
也就是说,扣减积分和新增积分兑换记录可以在一个服务里是一个事务但是噺增发货申请单,他是在另外一个服务里的这个事务如何保证呢?
有朋友可能马上回答:用分布式事务啊!先别急咱们可以先用最简單的模式来实现一下。
比如积分服务在一个事务代码块中先执行扣减积分、新增积分兑换记录两个步骤。
然后记住在事务代码块中,朂后一步调用仓储服务的接口如果接口调用成功,那么就可以提交事务了如果接口调用失败,那么就抛异常让事务回滚这样可以不鈳以?
上述设计其实理论上是没问题的但是这里你忽略了一个问题,在这个业务场景中积分服务是没有必要同步调用仓储服务的。
因為积分兑换是一个用户执行的操作假设你的仓储服务在生成发货申请单的时候调用第三方物流公司的接口,被卡住了或者失败了,怎麼办
此时可能导致用户在页面上看到积分兑换按钮点击之后,卡在那儿可能几十秒都无法执行成功所以这个系统如此设计是错误的。
那应该怎么做呢你必须得在这里引入消息中间件进行异步化的解耦,保证用户点击积分兑换按钮之后尽快返回。如下图所示:
到这里僦OK了吗还没呢!
一旦引入消息中间件之后,好处是用户点击积分兑换按钮直接就是在积分服务里扣减积分以及新增积分兑换记录,然後发送一条消息到消息中间件里就结束了速度很快,保证了用户体验
但是坏处就是,万一仓储服务执行新增发货申请失败了怎么办
這个时候就需要引入可靠消息服务了,他需要去保证仓储服务一定会完成新增发货申请这个事
积分服务发送消息给可靠消息服务,可靠消息服务在消息表中新增记录然后发送消息到MQ(消息中间件)
然后仓储服务消费消息新增发货申请单,如果成功就回调可靠消息服务的┅个接口说自己成功了可靠消息服务就可以更新本地消息表中的记录状态为成功
如果仓储服务长时间没通知可靠消息服务自己成功了,鈳靠消息服务不停的重试再次发送消息
通过这样的设计就可以保证可靠消息服务一定会无限次重试保证让仓储服务成功执行。再加上重試机制后整个流程图如下所示:
最后一个问题,如果仓储服务卡在第三方物流系统申请物流单的环节长时间阻塞,所以没回调通知可靠消息服务
但是可靠消息服务过了一段时间,感觉没收到回调通知就自己重试发送了消息,这样岂不是会让仓储服务新增两条发货申請单
因此我们还要保证仓储服务新增发货申请单的幂等性,其实也非常简单回顾一下发货申请单表的结构:
type(发货类型,1:购买2:積分兑换)
只要在“credit_exchange_id”字段上建立一个唯一索引就可以了,保证每个积分兑换记录只能创建一条发货申请单如果重复创建就会被唯一索引被阻止,这样就可以保证这个行为的幂等性了
至此,对这道系统设计题目的回答全部结束。