架构实战营 - 毕业设计
设计电商秒杀系统
【业务背景】你作为一个电商创业公司的架构师,负责设计 6.18 大促秒杀系统的设计,你们的业务模式如下:
你们挑选选品各大电商平台上畅销和好评的商品进行销售,每个品类不超过 20 个商品,目前做了 10 个品类;
本次 6.18 秒杀选择了 1000 个充电宝,10 台 iPhone 12 作为秒杀商品;
正常的日活大约 100 万用户;
老板要求万无一失。【技术背景】
技术团队以 Java 为主,已经落地了微服务架构;
主要渠道是自有的 App(包括 iOS 和 Android)和微信小程序,为了促进用户转化为 App 用户,只有下载 App 才能参加秒杀活动;
目前只有单机房。【毕设要求】
设计完整的架构,例如存储、负载均衡、缓存、高可用、可扩展等;
大约 10 页以内的 PPT,每页 PPT 说明一项设计,包括架构设计和设计理由,无需详细解释备选方案。【提示】
分析考虑要全面,但并不意味着架构设计要面面俱到,如果分析后认为某些设计点可以不做,就在作业最后统一说明原因即可;
如果没有思路,请对照模块 9 的 IM 案例;
如果有的信息觉得不够全或者不够细,可以做出一定的假设,但不能天马行空,需要对照已有的业务背景和技术背景进行合理推断。
架构设计
一、业务基本场景

1、用户可以通过自有 APP 浏览商城或者通过微信小程序浏览。
2、用户必须注册登录后才能进行下单和秒杀行为,否则会跳转至注册/登录页面。
3、秒杀商品只针对自有 APP 用户开放,对想参与秒杀的小程序平台用户进行引流至 app 注册账号。
4、用户将商品加入购物车后点击结算生成订单。
5、支付完成后订单生效,安排后续物流发货。
6、秒杀场景下,每个用户针对每种商品只能抢购一件,当秒杀商品库存为 0 时,不能进行抢购。
7、秒杀抢购场景下,用户抢到商品后生成订单,订单处于未付款状态时具有时效性,默认 15 分钟,15 分钟内没有付款则订单作废,订单作废后商品库存要恢复一件。
二、性能估算

【注册】百万级用户注册,正常日活用户 100W 左右,由于是购物类网站,按照实际注册人数 20%作为活跃用户,那么实际注册人数可估算为 500W 人
【登录】业务已给出正常日活用户为 100 万,那么登录每天有 100W 次登录请求。
【商品数量】商品数量不会太多,按照要求 10 个品类每个品类不超过 20 个商品,总商品数量是 10*20=200 个商品
【PV】按平均每个用户浏览网站点击 40 次左右,那么网站的日 pv 量大概是 4000W,用户每天浏览的时间段大概集中在 3~4 个小时内,那么 QPS = 4000W ÷ (3600×3) ≈ 3704,也就是网站流量 QPS 为 3704
【订单量】按照用户付费转化率 10%来计算,那么 100W*10%=10W,一天有 10W 订单数。日常情况下订单产生时间基本上集中在 3~4 个小时内,TPS = 10W ÷(3600×3) ≈ 9.25,也就是日常订单的写入速度是每秒 10 单。如果是大促集中在活动的前 5 分钟内,那么 TPS = 10W÷300 ≈ 334,也就是大促期间订单的写入速度是每秒 334 单。
【添加购物车/收藏】按照日活 100W 用户计算,假设每天每个用户有 2 次的收藏商品或者添加商品到购物车行为,那么 tps 为 100W × 2 ÷(3600×3)= 186
【秒杀】秒杀活动时可能会吸引大量用户参加,即平时不活跃用户可能也会登录 APP 抢购,因此我们按照注册人数 70%来计算抢购人数,得出 500W × 70% = 350W 人参加秒杀活动。本次秒杀商品为充电宝和 iphone,假设在某一时刻开抢,那么秒杀的 QPS 请求数量是 350W。
三、存储架构设计

四、计算架构之负载均衡

这里主要挑战就是面对秒杀活动的 350w 用户的并发请求,这里采用 LVS 集群+Nginx 集群,主要通过 DNS 将前端流量导入不同的 LVS 服务器,再由 LVS 服务器将流量进一步分发给不同的 NG 服务器,通过层层分发流量来消化大规模的并发请求,最后利用 NG 的缓存,商品售罄的消息在 ng 这一层返回大部分没有秒杀成功的用户。
五、计算架构之缓存架构

1、我们需要把秒杀商品的库存提前预热到分布式缓存 redis 中,通过 redis 提供的原子扣减来完成库存预减操作。
2、还需要提前把秒杀商品的相关静态资源放入 CDN 中,应对大量页面静态资源请求。
3、缓存架构为了应对秒杀下商品售罄后的无效订单请求,需要把售罄标识放入 Nginx openrestry,将无效请求尽量挡在 nginx 接入层,以提高处理效率。同时秒杀结束后还需要将 CDN 中的秒杀按钮置灰(更新 CDN 缓存)来表示秒杀活动结束,避免用户从前端再发起请求。
六、可扩展架构之微服务拆分
公司已落地微服务架构,也就是说电商的基本微服务都已存在,秒杀系统可以复用电商的通用服务(比如物流、商品、用户等)。这里不再做重复设计。
秒杀服务一般要单独独立出来(通过路由策略分发到单独的集群处理),避免秒杀活动的高并发请求,拖垮正常业务服务。
七、高可用架构

1、老板要求要确保万无一失,但是目前只有单机房,如果出现机房级别的灾难,单机房是无法做到高可用的。我们可以吧秒杀业务服务,部署在云服务器上,可以避免机房级别灾难导致业务服务不可用。
2、将秒杀业务服务部署在云服务器上还有个好处,因为秒杀活动不是日常业务,通常在某些时间点才进行的活动,在平时秒杀业务的机器性能处于闲置状态,而部署在云端可以借助云服务厂商提供的弹性扩缩容机制,在秒杀开始高峰期间,弹性扩容机器应对激增流量,而平时则收回闲置机器,更加合理地成本的运用资源。
3、秒杀期间流量激增,为了保证业务服务器不被大流量击垮,需要采取一些保底措施,如通过限流熔断降级手段,减少业务集群被大规模并发请求压垮的概率,且辅助消息队列异步处理氢气,对流量进行削峰。
八、秒杀系统要解决的核心复杂度
问题:
1、秒杀系统核心问题在于,秒杀开始时,要处理大量用户在同一时间发出的购买请求,本业务场景下我们估算是 350W 的 QPS,但即使我们使用 LVS+Nginx 的负载均衡架构,如果一下处理 350W 的 QPS 还是有些力不从心。
2、我们还看到,虽然有大规模的并发请求,但是我们此次秒杀系统仅提供了 1010 件商品参与秒杀,也就是 350W 用户生成的购买请求,对于我们真正有效的请求只有 1010 个可以生成订单,绝大部分的请求最后都是返回给用户秒杀失败的消息。
3、虽然我们把售罄标志提前到 ng 的缓存中进行返回,但是对于 LVS 和 NG 还是压力很大。
4、综上我们得出秒杀系统解决的核心复杂度问题是将 350W 请求尽量进行错峰或者在 APP 端就过滤一部分
解决方案:
方案 1:我们可以在前端设置秒杀答题,这样可以进行让用户进行错峰请求,但是可能在用户体验上不太友好。
方案 2:提前发 Token。可以在秒杀前设置一个预约活动。 在活动中提前发放 token。例如一个秒杀活动有 1010 个商品,那就可以预先准备 20W 个 token。用户进行预约时,只发放 20W 个 Token,其他人也能预约成功,但是其实没有获得 token,那后面的秒杀,直接通过这个 token 就可以过滤掉一大部分人。大大降低了秒杀活动的并发量和开发难度。
这里我建议采取方案 2 的方式,其实是通过业务上的策略去解决技术难点问题。
评论