秒杀系统中的扣减库存和流量削峰

 

前言

上篇文章我们一起讨论了秒杀系统下,通过堆加机器解决高并发的方案有什么缺点,又讨论了使用多级缓存架构构建静态化页面,来减轻前端页面服务器压力的方式。

今天我们就接着往下讨论,小伙伴们可以看一下上一篇文章做个复习,讨论一下秒杀系统的技术难点与解决方案

我们先回顾一下场景。

假如我们的系统在00:00有一场秒杀活动,那么会有大量用户会提前几分钟开始刷新页面,这部分的解决方案上篇文章已经提出。

紧接着就是到了00:00之后,页面上可能会出现一个按钮,大量用户就会通过点击按钮开始向后台发送请求,抢购商品。

抢购到之后还要有下订单、支付、减库存等后续一系列的流程,所以不对这些操作进行优化,直接操作数据库,系统的压力一定是很大的。

接下来我们就针对这个问题一起看看如何解决吧。

 

验证用户身份

首先我们思考一个问题,对于秒杀活动,会不会有人作弊呢?

比如写好一段代码,在秒杀活动开始之前就不停的循环刷新页面,抢购商品。

王子告诉大家,这种情况是一定会发生的。其实我们看看各种抢票软件就明白了,每次高峰期抢票不也会有很多的渠道去刷票吗,这么看来12306能支持这么多的并发确实做得还不错。

那么如何针对这种作弊的行为呢,其实我们可以在秒杀成功之后做一个验证用户身份的功能,保证你是人而不是代码。

方式可以是弹出框做个验证码验证,或者做个答题功能,需要人工答对之后才能进行下一步的操作。

这个办法是非常有效的,不仅可以对作弊行为进行过滤,而且每个人回答的速度是不一样的,所以用户发起的请求就不会全部的积压在一个时间点上。

 

独立的秒杀系统集群

身份验证过后,用户就会把大量的请求发送到我们的订单系统中,那么问题来了,在秒杀活动中发起的海量请求,是要发给我们平时运行时使用的同一个订单系统集群中吗?

我们来思考一个问题,假如秒杀业务和平时的业务使用的是一套订单系统集群,那么在秒杀活动的时候,可能有海量的用户来参加秒杀活动,但是同样也有不会参加秒杀活动的用户在同时订购商品。

那么当秒杀开始的时候,订单服务器压力会剧增,普通用户也发起请求到订单服务器,这样会发生什么呢?

很可能服务器由于秒杀系统带来的压力,性能变差,那么普通用户在进行正常的下订单操作时也会发现系统运行缓慢。

所以我们要单独部署一套秒杀系统集群,单独处理秒杀业务,从而不影响正常业务的性能。

而且单独的秒杀系统集群也更容易做一些特殊的架构优化,说到这里,架构图如下:

 

 

扣减库存的优化

后台系统在用户抢购成功后,应该先做什么操作呢?

第一步操作就是扣减库存,因为大家知道,参与秒杀活动的商品都是有数量限制的,所以大量用户抢购成功后的第一步操作就是扣减库存。

那么如何进行扣减库存的操作呢?

小伙伴们可能会回答,可以在秒杀系统集群中调用库存系统接口,连接数据库,更新库存数量。但这样一来不就又面临着数据库压力过大的问题了吗?

其实我们可以在活动开始前,把要秒杀的商品库存存放到Redis集群中,然后扣减库存的时候只操作Redis集群,就可以大大降低数据库压力了。

当商品的库存扣减完毕之后,用户发送过来抢购的请求其实就不必再发送给秒杀系统了,可以直接在Nginx中过滤掉。

Nginx具体如何过滤呢?这里王子提出一点思路,我们可以通过Zookeeper来实现。

当商品库存为0后,我们可以在Zookeeper中设置一个标志,表名商品已经售空了,同时可以利用Zookeeper的监听机制,告知Nginx的lua脚本,然后Lua脚本直接过滤掉无效的请求,并返回用户一个“库存已售空”的响应信息就可以了。

这样可以很大幅度的减少海量请求对后台秒杀系统的压力。

 

引入RocketMQ进行流量削峰

通过之前的优化,已经过滤掉了大量的无用请求,那么针对正常参加秒杀,发送给后台的请求我们应该怎么进行架构优化呢?

这个时候我们就可以引入RocketMQ,来进行流量削峰了

也就是说,当用户发送请求,经过Redis扣减库存的操作后发现库存数量还是大于0的,那么这个时候就可以把创建订单的操作发送消息给RocketMQ,然后我们平时使用的订单系统从RocketMQ中限流获取消息,进行常规的操作(生成订单、支付等等)。这样就不会对数据库有太大的压力了。

由于订单系统限流获取消息,所以会造成RockeMQ的消息积压问题,但RocketMQ是高可用的集群,可以保证消息的不丢失。所以完全可以让订单系统每秒几千条的速度去消费,顶多可能会延迟个几十秒才会生成订单而已。

所以我们最后的架构图如下:

 

总结

到这里,相信小伙伴们对于秒杀系统的架构方案已经有了一个整体的了解了。

其实总结起来,秒杀系统的架构优化核心就是:单独部署抗下高并发的服务器集群,阻止高并发请求访问数据库

因为数据库是整个系统架构中的性能瓶颈,不可能无限扩展数据库服务器的数量来抗下高并发请求。而且不是所有时候系统都有这么高的并发的,扩展数据库并不划算。

还有在这里王子要说明一点,一套完整的秒杀系统架构体系是很复杂的,我们只是通过这么一个秒杀业务的场景,让小伙伴们感受到消息中间件在这样的架构中扮演的角色。

后续的文章里,我们还会一起深入的探讨消息中间件的底层原理,让我们一起进步吧。

 

 

往期文章推荐:

中间件专辑:

什么是消息中间件?主要作用是什么?

常见的消息中间件有哪些?你们是怎么进行技术选型的?

你懂RocketMQ 的架构原理吗?

聊一聊RocketMQ的注册中心NameServer

Broker的主从架构是怎么实现的?

RocketMQ生产部署架构如何设计

RabbitMQ和Kafka的高可用集群原理

RocketMQ的发送模式和消费模式

讨论一下秒杀系统的技术难点与解决方案

 

Tags: