问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

揭秘秒杀架构设计核心要点!

创作时间:
作者:
@小白创作中心

揭秘秒杀架构设计核心要点!

引用
CSDN
1.
https://m.blog.csdn.net/G_whang/article/details/146169426

0.前言

凌晨三点,手机屏幕突然亮起。运维工程师小王盯着监控大屏上陡然升起的流量曲线,手指在键盘上疾速飞舞——又一场秒杀战役打响了。这样的场景在电商平台周而复始,而支撑这场没有硝烟战争的核心,是一套精密如瑞士钟表的秒杀架构体系。

当百万级请求在瞬间同时叩击服务器大门,常规架构会像被洪水冲垮的堤坝般瞬间崩溃。真正的秒杀系统如同精密的泄洪闸,必须设计三级缓冲机制:前端静态化页面拦截60%的点击,中间层令牌桶过滤30%无效请求,最终仅有10%的真实流量渗透到核心交易区。

1.思路

对于秒杀架构设计而言,其难点在于僧多粥少,因此设计秒杀架构时,我们一般需要遵循:东西不能超卖、下单成功的订单数据不能丢失、服务器和数据库不能挂、尽量别让机器人抢走商品这 4 个原则。

其实,秒杀架构的设计方案就是一个不断过滤请求的过程。 从系统架构层面来说,秒杀系统的分层设计思路如下图所示:

秒杀系统的架构设计目标是尽量在上层把用户的请求处理掉,不让其往下层游动。

2.秒杀系统的业务步骤和对应方案解析

秒杀系统的具体业务流程,如下图所示:

接下来,我们按照秒杀架构系统的业务流程一步步讲解如何将请求拦截在系统上游。

1.浏览页面如何将请求拦截在上游?

静态资源能上 CDN 就上 CDN。如果涉及 PC 网站,首先必须前后端分离,然后静态资源能上 CDN 就上 CDN。使用 CDN 的好处是不浪费自己的服务器资源和带宽,且响应速度快。通过这种方式,我们可以把静态资源的压力拦截在系统分层的外面。

如果是动态的请求有以下3个实现方案:

比如评论、商品属性详情、购买数等请求,平时我们都是通过 JS 后台动态调用。在这个场景中,我们可以把动态的数据与页面进行整合,比如把每个秒杀商品的详情页面变成静态页面,然后再放入 CDN。如果嫌改造太大,我们也可以把它放在 Redis 缓存中,不过我更倾向于 CDN。

判断服务器时间开启秒杀的标识:一般页面中都有一个 JS,它通过访问服务器获取服务器时间,然后根据时间开启秒杀下单的按钮,即判断秒杀开始时,我们会将下单按钮设置为可以购买。针对获取服务器时间的这个请求,我们把它放在静态资源或负载均衡那层即可,这样用户请求就不会进入系统下游。

判断秒杀结束:我们的做法是将秒杀结束的标识放在 cookie 中,如果 cookie 中没有结束标识,请求就会进入后台服务器,后台服务器判断本地内存没有结束标识,就会进入缓存中,要是缓存中也没有结束标识,那就说明秒杀没结束。

2.下单页面如何将请求拦截在上游?

用户进入下单页时,主要分为 2 个操作动作:进入下单页、订单提交。

进入下单页:

为了防止别人通过爬虫抓取下单页页面信息,从而给服务器增加压力,因此我们需要在下单页中做以下 2 层防护,以此防止(恶意)请求重复提交。

页面 URL 后台动态获取:按照正常的活动设计流程,用户只有在秒杀活动开启后才可进入下单页,但难免有同行在活动开启前直接获取下单页的 URL 并不断刷新,这样恶意请求就跑到了后台服务器中。虽然后台服务器也可以拦截恶意请求,但是徒增了不少压力。此时我们主要使用一个诡异的 URL 进行处理。(我们不把它放在静态页面中,而是通过后台动态获取。)前面我们介绍了 JS 可以用来判断秒杀开始时间 ,秒杀时间一到,它便可以通过另一个请求获取这个 URL。

用户点击下单页的购买按钮直接 disable,因此我们还需要防止用户不断点击购买按钮。

订单提交:

秒杀系统架构方案的重心是订单提交,因为订单提交这个步骤的逻辑最复杂,其他步骤仅是页面展示的逻辑,针对高并发问题使用缓存或者 CDN 进行处理难度不大。因此,在订单提交环节,我们要想尽一切办法在系统各个分层中把一些不必要的请求过滤掉。

1.网关层面过滤请求:可以通过以下方式来过滤网关层面的请求,如果能过滤95%以上的请求,那么服务器可以说是非常稳定。

限定每个用户访问频率:比如每 5 秒下单 1 次。

限定每个 IP 访问频率:这种方式我们担心有些人通过机器人自动下单,因此错杀真实用户。

把一个时间段内的请求拦截掉一个百分比,或者只允许特定数量的请求进入后台服务器。(这里我们可以使用限流的漏桶或令牌桶算法)

2.后台服务器处理订单:请求进入后台服务器后,我们的目标已经不是如何过滤请求了,而是如何保证特价商品不超卖,以及如何保证特价商品订单数据的准确性。我们通过以下3种方式来处理:

商品库存放入缓存 Redis 中: 如果每个请求都前往数据库查询商品库存,数据库肯定扛不住,因此我们需要把库存存放在缓存中,这样每次用户下单前,先使用 decr 扣减库存,判断返回值。如果 Redis 的库存扣减后 <0,说明秒杀失败,库存 incr 回去;如果 Redis 的库存扣减后 >=0,说明秒杀成功,开始创建订单。

订单写入缓存中: 订单数据先不放入数据库,而是先放到缓存中,然后每隔一段时间(比如 100ms )批量插入订单。我们知道用户下单后,首先进入一个等待页面,然后这个页面向后台定时轮询订单数据。轮询订单数据的过程中,后台先在 Redis 中查询订单数据,查不到说明已经落库,再去数据库查询订单数据,查到后直接返回给用户,用户收到消息通知后可以直接进入付款页面支付了。在数据库查询订单数据时,查不到说明秒杀失败。

订单批量落库: 我们需要定期将订单批量落库,且在订单落库的时扣减数据库中的库存。

3.付款页如何将请求拦截在上游?

在付款页面,我们基本不需要再过滤用户请求了。在这个环节,我们除了保障数据的一致性,还需要注意一个要点:如果业务逻辑上出现一个订单未及时付款而被取消,记得把数据库及 Redis 的库存加回去。

3.整体架构设计

为了保障秒杀系统的高可用性,在整体服务器架构中,我们需要保证上图中所有的层级都是高可用。因此,静态资源服务器、网关、后台服务器均需要配置负载均衡,而缓存 Redis 和数据库均需要配置集群模式。

4.最后

秒杀成功率 = 有效请求过滤率 × 系统吞吐量 × 资源隔离强度

需通过 流量分层、异步化、热点优化、熔断降级 四板斧实现极致性能。建议秒杀开始前结合平台活跃用户评估参加活动的用户数量,然后使用压测工具提前验证结果并持续调优。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号
揭秘秒杀架构设计核心要点!