模块九 - 设计电商秒杀系统
1、项目背景
业务背景
你们挑选选品各大电商平台上畅销和好评的商品进行销售,每个品类不超过 20 个商品,目前做了 10 个品类;
本次 6.18 秒杀选择了 1000 个充电宝,10 台 iPhone 12 作为秒杀商品;
正常的日活大约 100 万用户;
老板要求万无一失。
技术背景
技术团队以 Java 为主,已经落地了微服务架构;
主要渠道是自有的 App(包括 iOS 和 Android)和微信小程序,为了促进用户转化为 App 用户,只有下载 App 才能参加秒杀活动;
目前只有单机房。
2、需求分析
1、由于已有 java 技术团队,且已经落地了微服务架构。登录注册、购物、库存管理等相关内容不做考虑,认为需要增加的只是一个秒杀服务,其余功能依赖现有微服务。
2、已知平台正常日活 100W 左右,虽然不是所有用户都会参加秒杀,但是由于活动时日活相对增加且老板要求万无一失,那么秒杀系统依旧按照百万级考虑。
3、由于秒杀的目的是为 APP 引流,因此与 APP 端正常显示秒杀活动不同,小程序端需要显示引流信息甚至 APP 下载链接。
4、秒杀活动不能影响用户正常的购物体验。
3、性能估算
已知准备的秒杀商品为 1000+10,假定活动吸引了 150W 用户参与秒杀且 2 秒内秒杀结束,秒杀过程中平均每个用户会发起三次秒杀请求,假定参与秒杀的用户会在秒杀开始的 1 分钟内访问活动页面等待秒杀倒计时。由于需要统一秒杀时间,所以需要用户向服务端请求倒计时信息。
秒杀开始时 qps 可以粗略估计为 150W,秒杀过程中 TPS 估计为 150W*3=450W。
因此,需要对秒杀时的请求进行限流等操作。
4、总体架构
1、负载均衡
考虑到系统访问量很大,负载均衡使用 lvs 进行多级负载均衡,即 DNS->LVS(集群)->nginx(集群)->服务接口(集群)的方式进行负载。算法使用轮询、随机均可。
![](https://static001.geekbang.org/infoq/2d/2ddb6a6fc57628b9887e8336fb300fc4.png)
2、缓存架构
App 缓存可以缓存一些秒杀活动的通知或者导航页面。
已知秒杀活动中,参与用户人数众多且可能会刷新秒杀页面,产生很大的流量影响正常购物,所以 CDN 缓存静态资源必不可少。
抢购服务集群很大,相关抢购信息可以使用分布式缓存(redis cluster),方便服务判断抢购是否成功。
![](https://static001.geekbang.org/infoq/71/71123796e8b75c7e7a020b3d2698f2ca.png)
3、可扩展
已知已经存在一个落地的微服务架构的商城系统,那么合理推断,用户、商品、订单、支付等服务已经存在。秒杀系统作为一个单独的服务依赖于已有的微服务架构即可,不需要再进行服务拆分。但是考虑到秒杀活动会造成商城流量增加,因此可能需要对现有的服务集群进行扩展。
4、高可用
考虑到要求秒杀活动万无一失,且同城机房可以认为是同一个逻辑机房。那么如果在预算允许的前提下可以考虑使用同城双活的方式对服务进行部署。但是极端情况下会出现双活机房通信异常可能存在脑裂的异常情况,但是最大的损失可能是抢购商品超卖一倍。
5、存储
秒杀系统最终入库的可能只是实际发生交易的 1010 个订单记录而已。对于数据库的压力极小,利用现有的商城服务数据库即可。如果必须单独保存秒杀信息的话,使用 mysql 主备即可。
![](https://static001.geekbang.org/infoq/3a/3ad03fe81f8e136f2aa62a849314d42f.png)
6、限流
由于秒杀时段瞬时流量巨大,服务端不可能正常处理。又因为最终的商品相对于参与用户极少,因此需要对秒杀的请求进行逐级限流减轻服务器压力。
前端:
1、前端动态效果限制用户点击抢购的频率,如按钮置灰或者使用过场动画遮罩等。
2、主动抛弃请求,js 控制使用一些算法直接抛弃秒杀的请求,防止过多流量进入服务端。
服务端:
1、利用 redis 缓存标记用户主键,当出现用户请求时进行标记,根据标记拒绝重复请求。
2、利用令牌桶算法,提前生成秒杀商品的令牌,秒杀请求进入服务端后直接请求令牌,成功则进入正常的支付页面。请求不到令牌则直接返回秒杀失败。限制流量到达数据库。
评论