一次阿里面试,我被问到了如何设计秒杀系统

2022-05-05 18:52:29 浏览数 (1)

秒杀活动架构

秒杀活动是电商项目中常出现的活动。比如演唱会门票抢购,京东淘宝秒杀商品抢购。在抢购那一刻,会有大量用户同时高并发的请求应用系统,可能会达到每秒几万、几十万的请求。如果系统无法处理这么高的请求,那么就会崩溃,从而导致系统不可用。

对于秒杀活动来说,要求系统不会出现压力过大而崩溃的场景,并且不会出现超卖、少卖的情形。

所以秒杀系统中我们需要思考:

  • 系统如何扛住高并发请求
  • 系统如何保证不超卖等问题

对此我的解决思路是:

  • 服务端中,使用缓存减少对数据库访问
  • 将请求流量拦截在上游,可以使用限流技术
  • 使用分布式队列进行流量削峰,将并发串行化

秒杀架构图

秒杀架构图如上。

客户端

用户发起请求的端口,目前电商项目秒杀活动主要客户端有微信小程序、H5(浏览器)、各平台app(比如Android、iOS、Windows)。我们可以在客户端实现限流机制,这样就可以避免用户发送大量请求到服务端。

客户端的限流可以控制按钮的点击频率,比如对按钮置灰。

反向代理

我们可以使用Nginx实现请求分流,通过负载均衡将请求均匀的分布到不同的Web节点中。

Nginx也可以作为限流使用。Nginx可以控制单位时间内的请求数,限制同一时间的连接数。

API网关

如果实际参与秒杀活动的用户非常大,并发请求非常大。我们就需要在API网关这一层中进行限流,这里可以实现对单个Web节点实现每秒最大请求数限制。

我们也可以控制每个用户的最大请求数,通过Redis记录每个用户的请求数。

缓存

在服务层业务中,为减少对数据库的访问,需要进行缓存设计,我们可以使用本地缓存,或者分布式缓存。

队列

队列主要是用来实现流量的削峰填谷,我们可以使用RocketMQ、Kafka等消息中间件作为分布式的队列。

关于限流

秒杀系统中为什么需要限流?在秒杀活动中商品库存是有限的,而请求的用户数量远远大于商品库存数量。大部分的用户请求实际上是无法抢到商品的无效流量。所以这部分流量可以拦截在上游进行限流。

为了避免用户采用脚本或者频繁点击发送大量请求,而导致其他用户无法正常参加活动,我们也需要控制用户每秒的请求量。

我们可从URI和用户两个方面实行限流。

URI限流示例代码:

用户维度的限流示例代码:

我们获取URI维度的限流器对象uriLimiter,和用户维度的限流器对象userLimiter。如果tryAcquire()成功获得许可,则请求通过,进入后续业务。否则直接报异常,前端界面作相应的友好提示。

在缓存中扣减库存

使用Redis来存储库存数量,当用户发起抢购请求时,先判断Redis中的库存是否可用。如果可用,将抢购请求放入分布式队列中,采用异步方式处理后续操作,并完成下单。同时在Redis中作库存扣减。

示例代码如下:

先做库存扣减并获取扣减后的库存数量,如果库存数量大于或等于0,将订单创建请求发送到mq。否则返回抢购失败的信息。

消费者创建订单:

关于如何初始化库存?

在抢购活动开始前,有运营人员在后台手动将商品库存从数据库同步到缓存中。库存的扣减在缓存中进行扣减。

利用Redis单线程特性可以实现多线程下安全的库存更新。如果查询缓存中的值时,发现没有,需要从数据库中查询,然后再同步到缓存中,这个过程需要加锁。

示例代码如下:

总结

秒杀设计是典型的高并发系统,关于秒杀系统的设计,经常会在面试中被问到。在设计秒杀系统时,我们需要考虑:

  • 请求分流-通过负载均衡实现
  • 限流-采用漏斗算法(如guava的RateLimiter),信号量Semaphore,Redis计数等实现
  • Redis扣减库存-减少对数据库的访问
  • 流量削峰-分布式队列实现

好啦,本篇到这里就结束了,希望文章对你有帮助。

0 人点赞